Commit 752f114f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'tracing-core-for-linus' of...

Merge branch 'tracing-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'tracing-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  tracing: Fix "integer as NULL pointer" warning.
  tracing: Fix tracepoint.h DECLARE_TRACE() to allow more than one header
  tracing: Make the documentation clear on trace_event boot option
  ring-buffer: Wrap open-coded WARN_ONCE
  tracing: Convert nop macros to static inlines
  tracing: Fix sleep time function profiling
  tracing: Show sample std dev in function profiling
  tracing: Add documentation for trace commands mod, traceon/traceoff
  ring-buffer: Make benchmark handle missed events
  ring-buffer: Make non-consuming read less expensive with lots of cpus.
  tracing: Add graph output support for irqsoff tracer
  tracing: Have graph flags passed in to ouput functions
  tracing: Add ftrace events for graph tracer
  tracing: Dump either the oops's cpu source or all cpus buffers
  tracing: Fix uninitialized variable of tracing/trace output
parents b8ae30ee ad56b079
...@@ -786,8 +786,12 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -786,8 +786,12 @@ and is between 256 and 4096 characters. It is defined in the file
as early as possible in order to facilitate early as early as possible in order to facilitate early
boot debugging. boot debugging.
ftrace_dump_on_oops ftrace_dump_on_oops[=orig_cpu]
[FTRACE] will dump the trace buffers on oops. [FTRACE] will dump the trace buffers on oops.
If no parameter is passed, ftrace will dump
buffers of all CPUs, but if you pass orig_cpu, it will
dump only the buffer of the CPU that triggered the
oops.
ftrace_filter=[function-list] ftrace_filter=[function-list]
[FTRACE] Limit the functions traced by the function [FTRACE] Limit the functions traced by the function
......
...@@ -90,7 +90,8 @@ In order to facilitate early boot debugging, use boot option: ...@@ -90,7 +90,8 @@ In order to facilitate early boot debugging, use boot option:
trace_event=[event-list] trace_event=[event-list]
The format of this boot option is the same as described in section 2.1. event-list is a comma separated list of events. See section 2.1 for event
format.
3. Defining an event-enabled tracepoint 3. Defining an event-enabled tracepoint
======================================= =======================================
......
...@@ -155,6 +155,9 @@ of ftrace. Here is a list of some of the key files: ...@@ -155,6 +155,9 @@ of ftrace. Here is a list of some of the key files:
to be traced. Echoing names of functions into this file to be traced. Echoing names of functions into this file
will limit the trace to only those functions. will limit the trace to only those functions.
This interface also allows for commands to be used. See the
"Filter commands" section for more details.
set_ftrace_notrace: set_ftrace_notrace:
This has an effect opposite to that of This has an effect opposite to that of
...@@ -1337,12 +1340,14 @@ ftrace_dump_on_oops must be set. To set ftrace_dump_on_oops, one ...@@ -1337,12 +1340,14 @@ ftrace_dump_on_oops must be set. To set ftrace_dump_on_oops, one
can either use the sysctl function or set it via the proc system can either use the sysctl function or set it via the proc system
interface. interface.
sysctl kernel.ftrace_dump_on_oops=1 sysctl kernel.ftrace_dump_on_oops=n
or or
echo 1 > /proc/sys/kernel/ftrace_dump_on_oops echo n > /proc/sys/kernel/ftrace_dump_on_oops
If n = 1, ftrace will dump buffers of all CPUs, if n = 2 ftrace will
only dump the buffer of the CPU that triggered the oops.
Here's an example of such a dump after a null pointer Here's an example of such a dump after a null pointer
dereference in a kernel module: dereference in a kernel module:
...@@ -1822,6 +1827,47 @@ this special filter via: ...@@ -1822,6 +1827,47 @@ this special filter via:
echo > set_graph_function echo > set_graph_function
Filter commands
---------------
A few commands are supported by the set_ftrace_filter interface.
Trace commands have the following format:
<function>:<command>:<parameter>
The following commands are supported:
- mod
This command enables function filtering per module. The
parameter defines the module. For example, if only the write*
functions in the ext3 module are desired, run:
echo 'write*:mod:ext3' > set_ftrace_filter
This command interacts with the filter in the same way as
filtering based on function names. Thus, adding more functions
in a different module is accomplished by appending (>>) to the
filter file. Remove specific module functions by prepending
'!':
echo '!writeback*:mod:ext3' >> set_ftrace_filter
- traceon/traceoff
These commands turn tracing on and off when the specified
functions are hit. The parameter determines how many times the
tracing system is turned on and off. If unspecified, there is
no limit. For example, to disable tracing when a schedule bug
is hit the first 5 times, run:
echo '__schedule_bug:traceoff:5' > set_ftrace_filter
These commands are cumulative whether or not they are appended
to set_ftrace_filter. To remove a command, prepend it by '!'
and drop the parameter:
echo '!__schedule_bug:traceoff' > set_ftrace_filter
trace_pipe trace_pipe
---------- ----------
......
...@@ -289,7 +289,7 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = { ...@@ -289,7 +289,7 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = {
static void sysrq_ftrace_dump(int key, struct tty_struct *tty) static void sysrq_ftrace_dump(int key, struct tty_struct *tty)
{ {
ftrace_dump(); ftrace_dump(DUMP_ALL);
} }
static struct sysrq_key_op sysrq_ftrace_dump_op = { static struct sysrq_key_op sysrq_ftrace_dump_op = {
.handler = sysrq_ftrace_dump, .handler = sysrq_ftrace_dump,
......
...@@ -82,9 +82,13 @@ void clear_ftrace_function(void); ...@@ -82,9 +82,13 @@ void clear_ftrace_function(void);
extern void ftrace_stub(unsigned long a0, unsigned long a1); extern void ftrace_stub(unsigned long a0, unsigned long a1);
#else /* !CONFIG_FUNCTION_TRACER */ #else /* !CONFIG_FUNCTION_TRACER */
# define register_ftrace_function(ops) do { } while (0) /*
# define unregister_ftrace_function(ops) do { } while (0) * (un)register_ftrace_function must be a macro since the ops parameter
# define clear_ftrace_function(ops) do { } while (0) * must not be evaluated.
*/
#define register_ftrace_function(ops) ({ 0; })
#define unregister_ftrace_function(ops) ({ 0; })
static inline void clear_ftrace_function(void) { }
static inline void ftrace_kill(void) { } static inline void ftrace_kill(void) { }
static inline void ftrace_stop(void) { } static inline void ftrace_stop(void) { }
static inline void ftrace_start(void) { } static inline void ftrace_start(void) { }
...@@ -237,11 +241,13 @@ extern int skip_trace(unsigned long ip); ...@@ -237,11 +241,13 @@ extern int skip_trace(unsigned long ip);
extern void ftrace_disable_daemon(void); extern void ftrace_disable_daemon(void);
extern void ftrace_enable_daemon(void); extern void ftrace_enable_daemon(void);
#else #else
# define skip_trace(ip) ({ 0; }) static inline int skip_trace(unsigned long ip) { return 0; }
# define ftrace_force_update() ({ 0; }) static inline int ftrace_force_update(void) { return 0; }
# define ftrace_set_filter(buf, len, reset) do { } while (0) static inline void ftrace_set_filter(unsigned char *buf, int len, int reset)
# define ftrace_disable_daemon() do { } while (0) {
# define ftrace_enable_daemon() do { } while (0) }
static inline void ftrace_disable_daemon(void) { }
static inline void ftrace_enable_daemon(void) { }
static inline void ftrace_release_mod(struct module *mod) {} static inline void ftrace_release_mod(struct module *mod) {}
static inline int register_ftrace_command(struct ftrace_func_command *cmd) static inline int register_ftrace_command(struct ftrace_func_command *cmd)
{ {
...@@ -314,16 +320,16 @@ static inline void __ftrace_enabled_restore(int enabled) ...@@ -314,16 +320,16 @@ static inline void __ftrace_enabled_restore(int enabled)
extern void time_hardirqs_on(unsigned long a0, unsigned long a1); extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
extern void time_hardirqs_off(unsigned long a0, unsigned long a1); extern void time_hardirqs_off(unsigned long a0, unsigned long a1);
#else #else
# define time_hardirqs_on(a0, a1) do { } while (0) static inline void time_hardirqs_on(unsigned long a0, unsigned long a1) { }
# define time_hardirqs_off(a0, a1) do { } while (0) static inline void time_hardirqs_off(unsigned long a0, unsigned long a1) { }
#endif #endif
#ifdef CONFIG_PREEMPT_TRACER #ifdef CONFIG_PREEMPT_TRACER
extern void trace_preempt_on(unsigned long a0, unsigned long a1); extern void trace_preempt_on(unsigned long a0, unsigned long a1);
extern void trace_preempt_off(unsigned long a0, unsigned long a1); extern void trace_preempt_off(unsigned long a0, unsigned long a1);
#else #else
# define trace_preempt_on(a0, a1) do { } while (0) static inline void trace_preempt_on(unsigned long a0, unsigned long a1) { }
# define trace_preempt_off(a0, a1) do { } while (0) static inline void trace_preempt_off(unsigned long a0, unsigned long a1) { }
#endif #endif
#ifdef CONFIG_FTRACE_MCOUNT_RECORD #ifdef CONFIG_FTRACE_MCOUNT_RECORD
...@@ -352,6 +358,10 @@ struct ftrace_graph_ret { ...@@ -352,6 +358,10 @@ struct ftrace_graph_ret {
int depth; int depth;
}; };
/* Type of the callback handlers for tracing function graph*/
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */
typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
/* for init task */ /* for init task */
...@@ -400,10 +410,6 @@ extern char __irqentry_text_end[]; ...@@ -400,10 +410,6 @@ extern char __irqentry_text_end[];
#define FTRACE_RETFUNC_DEPTH 50 #define FTRACE_RETFUNC_DEPTH 50
#define FTRACE_RETSTACK_ALLOC_SIZE 32 #define FTRACE_RETSTACK_ALLOC_SIZE 32
/* Type of the callback handlers for tracing function graph*/
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */
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);
...@@ -441,6 +447,13 @@ static inline void unpause_graph_tracing(void) ...@@ -441,6 +447,13 @@ static inline void unpause_graph_tracing(void)
static inline void ftrace_graph_init_task(struct task_struct *t) { } static inline void ftrace_graph_init_task(struct task_struct *t) { }
static inline void ftrace_graph_exit_task(struct task_struct *t) { } static inline void ftrace_graph_exit_task(struct task_struct *t) { }
static inline int register_ftrace_graph(trace_func_graph_ret_t retfunc,
trace_func_graph_ent_t entryfunc)
{
return -1;
}
static inline void unregister_ftrace_graph(void) { }
static inline int task_curr_ret_stack(struct task_struct *tsk) static inline int task_curr_ret_stack(struct task_struct *tsk)
{ {
return -1; return -1;
...@@ -492,7 +505,9 @@ static inline int test_tsk_trace_graph(struct task_struct *tsk) ...@@ -492,7 +505,9 @@ static inline int test_tsk_trace_graph(struct task_struct *tsk)
return tsk->trace & TSK_TRACE_FL_GRAPH; return tsk->trace & TSK_TRACE_FL_GRAPH;
} }
extern int ftrace_dump_on_oops; enum ftrace_dump_mode;
extern enum ftrace_dump_mode ftrace_dump_on_oops;
#ifdef CONFIG_PREEMPT #ifdef CONFIG_PREEMPT
#define INIT_TRACE_RECURSION .trace_recursion = 0, #define INIT_TRACE_RECURSION .trace_recursion = 0,
......
...@@ -490,6 +490,13 @@ static inline void tracing_off(void) { } ...@@ -490,6 +490,13 @@ static inline void tracing_off(void) { }
static inline void tracing_off_permanent(void) { } static inline void tracing_off_permanent(void) { }
static inline int tracing_is_on(void) { return 0; } static inline int tracing_is_on(void) { return 0; }
#endif #endif
enum ftrace_dump_mode {
DUMP_NONE,
DUMP_ALL,
DUMP_ORIG,
};
#ifdef CONFIG_TRACING #ifdef CONFIG_TRACING
extern void tracing_start(void); extern void tracing_start(void);
extern void tracing_stop(void); extern void tracing_stop(void);
...@@ -571,7 +578,7 @@ __ftrace_vbprintk(unsigned long ip, const char *fmt, va_list ap); ...@@ -571,7 +578,7 @@ __ftrace_vbprintk(unsigned long ip, const char *fmt, va_list ap);
extern int extern int
__ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap); __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap);
extern void ftrace_dump(void); extern void ftrace_dump(enum ftrace_dump_mode oops_dump_mode);
#else #else
static inline void static inline void
ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { }
...@@ -592,7 +599,7 @@ ftrace_vprintk(const char *fmt, va_list ap) ...@@ -592,7 +599,7 @@ ftrace_vprintk(const char *fmt, va_list ap)
{ {
return 0; return 0;
} }
static inline void ftrace_dump(void) { } static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
#endif /* CONFIG_TRACING */ #endif /* CONFIG_TRACING */
/* /*
......
...@@ -127,7 +127,9 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts, ...@@ -127,7 +127,9 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts,
unsigned long *lost_events); unsigned long *lost_events);
struct ring_buffer_iter * struct ring_buffer_iter *
ring_buffer_read_start(struct ring_buffer *buffer, int cpu); ring_buffer_read_prepare(struct ring_buffer *buffer, int cpu);
void ring_buffer_read_prepare_sync(void);
void ring_buffer_read_start(struct ring_buffer_iter *iter);
void ring_buffer_read_finish(struct ring_buffer_iter *iter); void ring_buffer_read_finish(struct ring_buffer_iter *iter);
struct ring_buffer_event * struct ring_buffer_event *
......
...@@ -33,6 +33,65 @@ struct tracepoint { ...@@ -33,6 +33,65 @@ struct tracepoint {
* Keep in sync with vmlinux.lds.h. * Keep in sync with vmlinux.lds.h.
*/ */
/*
* Connect a probe to a tracepoint.
* Internal API, should not be used directly.
*/
extern int tracepoint_probe_register(const char *name, void *probe);
/*
* Disconnect a probe from a tracepoint.
* Internal API, should not be used directly.
*/
extern int tracepoint_probe_unregister(const char *name, void *probe);
extern int tracepoint_probe_register_noupdate(const char *name, void *probe);
extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe);
extern void tracepoint_probe_update_all(void);
struct tracepoint_iter {
struct module *module;
struct tracepoint *tracepoint;
};
extern void tracepoint_iter_start(struct tracepoint_iter *iter);
extern void tracepoint_iter_next(struct tracepoint_iter *iter);
extern void tracepoint_iter_stop(struct tracepoint_iter *iter);
extern void tracepoint_iter_reset(struct tracepoint_iter *iter);
extern int tracepoint_get_iter_range(struct tracepoint **tracepoint,
struct tracepoint *begin, struct tracepoint *end);
/*
* tracepoint_synchronize_unregister must be called between the last tracepoint
* probe unregistration and the end of module exit to make sure there is no
* caller executing a probe when it is freed.
*/
static inline void tracepoint_synchronize_unregister(void)
{
synchronize_sched();
}
#define PARAMS(args...) args
#ifdef CONFIG_TRACEPOINTS
extern void tracepoint_update_probe_range(struct tracepoint *begin,
struct tracepoint *end);
#else
static inline void tracepoint_update_probe_range(struct tracepoint *begin,
struct tracepoint *end)
{ }
#endif /* CONFIG_TRACEPOINTS */
#endif /* _LINUX_TRACEPOINT_H */
/*
* Note: we keep the TRACE_EVENT and DECLARE_TRACE outside the include
* file ifdef protection.
* This is due to the way trace events work. If a file includes two
* trace event headers under one "CREATE_TRACE_POINTS" the first include
* will override the TRACE_EVENT and break the second include.
*/
#ifndef DECLARE_TRACE #ifndef DECLARE_TRACE
#define TP_PROTO(args...) args #define TP_PROTO(args...) args
...@@ -96,9 +155,6 @@ struct tracepoint { ...@@ -96,9 +155,6 @@ struct tracepoint {
#define EXPORT_TRACEPOINT_SYMBOL(name) \ #define EXPORT_TRACEPOINT_SYMBOL(name) \
EXPORT_SYMBOL(__tracepoint_##name) EXPORT_SYMBOL(__tracepoint_##name)
extern void tracepoint_update_probe_range(struct tracepoint *begin,
struct tracepoint *end);
#else /* !CONFIG_TRACEPOINTS */ #else /* !CONFIG_TRACEPOINTS */
#define DECLARE_TRACE(name, proto, args) \ #define DECLARE_TRACE(name, proto, args) \
static inline void _do_trace_##name(struct tracepoint *tp, proto) \ static inline void _do_trace_##name(struct tracepoint *tp, proto) \
...@@ -119,61 +175,9 @@ extern void tracepoint_update_probe_range(struct tracepoint *begin, ...@@ -119,61 +175,9 @@ extern void tracepoint_update_probe_range(struct tracepoint *begin,
#define EXPORT_TRACEPOINT_SYMBOL_GPL(name) #define EXPORT_TRACEPOINT_SYMBOL_GPL(name)
#define EXPORT_TRACEPOINT_SYMBOL(name) #define EXPORT_TRACEPOINT_SYMBOL(name)
static inline void tracepoint_update_probe_range(struct tracepoint *begin,
struct tracepoint *end)
{ }
#endif /* CONFIG_TRACEPOINTS */ #endif /* CONFIG_TRACEPOINTS */
#endif /* DECLARE_TRACE */ #endif /* DECLARE_TRACE */
/*
* Connect a probe to a tracepoint.
* Internal API, should not be used directly.
*/
extern int tracepoint_probe_register(const char *name, void *probe);
/*
* Disconnect a probe from a tracepoint.
* Internal API, should not be used directly.
*/
extern int tracepoint_probe_unregister(const char *name, void *probe);
extern int tracepoint_probe_register_noupdate(const char *name, void *probe);
extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe);
extern void tracepoint_probe_update_all(void);
struct tracepoint_iter {
struct module *module;
struct tracepoint *tracepoint;
};
extern void tracepoint_iter_start(struct tracepoint_iter *iter);
extern void tracepoint_iter_next(struct tracepoint_iter *iter);
extern void tracepoint_iter_stop(struct tracepoint_iter *iter);
extern void tracepoint_iter_reset(struct tracepoint_iter *iter);
extern int tracepoint_get_iter_range(struct tracepoint **tracepoint,
struct tracepoint *begin, struct tracepoint *end);
/*
* tracepoint_synchronize_unregister must be called between the last tracepoint
* probe unregistration and the end of module exit to make sure there is no
* caller executing a probe when it is freed.
*/
static inline void tracepoint_synchronize_unregister(void)
{
synchronize_sched();
}
#define PARAMS(args...) args
#endif /* _LINUX_TRACEPOINT_H */
/*
* Note: we keep the TRACE_EVENT outside the include file ifdef protection.
* This is due to the way trace events work. If a file includes two
* trace event headers under one "CREATE_TRACE_POINTS" the first include
* will override the TRACE_EVENT and break the second include.
*/
#ifndef TRACE_EVENT #ifndef TRACE_EVENT
/* /*
* For use with the TRACE_EVENT macro: * For use with the TRACE_EVENT macro:
......
...@@ -65,6 +65,10 @@ ...@@ -65,6 +65,10 @@
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
/* Make all open coded DECLARE_TRACE nops */
#undef DECLARE_TRACE
#define DECLARE_TRACE(name, proto, args)
#ifdef CONFIG_EVENT_TRACING #ifdef CONFIG_EVENT_TRACING
#include <trace/ftrace.h> #include <trace/ftrace.h>
#endif #endif
...@@ -75,6 +79,7 @@ ...@@ -75,6 +79,7 @@
#undef DEFINE_EVENT #undef DEFINE_EVENT
#undef DEFINE_EVENT_PRINT #undef DEFINE_EVENT_PRINT
#undef TRACE_HEADER_MULTI_READ #undef TRACE_HEADER_MULTI_READ
#undef DECLARE_TRACE
/* Only undef what we defined in this file */ /* Only undef what we defined in this file */
#ifdef UNDEF_TRACE_INCLUDE_FILE #ifdef UNDEF_TRACE_INCLUDE_FILE
......
#ifndef _TRACE_NAPI_H_ #undef TRACE_SYSTEM
#define TRACE_SYSTEM napi
#if !defined(_TRACE_NAPI_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_NAPI_H_ #define _TRACE_NAPI_H_
#include <linux/netdevice.h> #include <linux/netdevice.h>
...@@ -8,4 +11,7 @@ DECLARE_TRACE(napi_poll, ...@@ -8,4 +11,7 @@ DECLARE_TRACE(napi_poll,
TP_PROTO(struct napi_struct *napi), TP_PROTO(struct napi_struct *napi),
TP_ARGS(napi)); TP_ARGS(napi));
#endif #endif /* _TRACE_NAPI_H_ */
/* This part must be outside protection */
#include <trace/define_trace.h>
...@@ -264,6 +264,7 @@ struct ftrace_profile { ...@@ -264,6 +264,7 @@ struct ftrace_profile {
unsigned long counter; unsigned long counter;
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
unsigned long long time; unsigned long long time;
unsigned long long time_squared;
#endif #endif
}; };
...@@ -366,9 +367,9 @@ static int function_stat_headers(struct seq_file *m) ...@@ -366,9 +367,9 @@ static int function_stat_headers(struct seq_file *m)
{ {
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
seq_printf(m, " Function " seq_printf(m, " Function "
"Hit Time Avg\n" "Hit Time Avg s^2\n"
" -------- " " -------- "
"--- ---- ---\n"); "--- ---- --- ---\n");
#else #else
seq_printf(m, " Function Hit\n" seq_printf(m, " Function Hit\n"
" -------- ---\n"); " -------- ---\n");
...@@ -384,6 +385,7 @@ static int function_stat_show(struct seq_file *m, void *v) ...@@ -384,6 +385,7 @@ static int function_stat_show(struct seq_file *m, void *v)
static DEFINE_MUTEX(mutex); static DEFINE_MUTEX(mutex);
static struct trace_seq s; static struct trace_seq s;
unsigned long long avg; unsigned long long avg;
unsigned long long stddev;
#endif #endif
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
...@@ -394,11 +396,25 @@ static int function_stat_show(struct seq_file *m, void *v) ...@@ -394,11 +396,25 @@ static int function_stat_show(struct seq_file *m, void *v)
avg = rec->time; avg = rec->time;
do_div(avg, rec->counter); do_div(avg, rec->counter);
/* Sample standard deviation (s^2) */
if (rec->counter <= 1)
stddev = 0;
else {
stddev = rec->time_squared - rec->counter * avg * avg;
/*
* Divide only 1000 for ns^2 -> us^2 conversion.
* trace_print_graph_duration will divide 1000 again.
*/
do_div(stddev, (rec->counter - 1) * 1000);
}
mutex_lock(&mutex); mutex_lock(&mutex);
trace_seq_init(&s); trace_seq_init(&s);
trace_print_graph_duration(rec->time, &s); trace_print_graph_duration(rec->time, &s);
trace_seq_puts(&s, " "); trace_seq_puts(&s, " ");
trace_print_graph_duration(avg, &s); trace_print_graph_duration(avg, &s);
trace_seq_puts(&s, " ");
trace_print_graph_duration(stddev, &s);
trace_print_seq(m, &s); trace_print_seq(m, &s);
mutex_unlock(&mutex); mutex_unlock(&mutex);
#endif #endif
...@@ -650,6 +666,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace) ...@@ -650,6 +666,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace)
if (!stat->hash || !ftrace_profile_enabled) if (!stat->hash || !ftrace_profile_enabled)
goto out; goto out;
/* If the calltime was zero'd ignore it */
if (!trace->calltime)
goto out;
calltime = trace->rettime - trace->calltime; calltime = trace->rettime - trace->calltime;
if (!(trace_flags & TRACE_ITER_GRAPH_TIME)) { if (!(trace_flags & TRACE_ITER_GRAPH_TIME)) {
...@@ -668,8 +688,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace) ...@@ -668,8 +688,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace)
} }
rec = ftrace_find_profiled_func(stat, trace->func); rec = ftrace_find_profiled_func(stat, trace->func);
if (rec) if (rec) {
rec->time += calltime; rec->time += calltime;
rec->time_squared += calltime * calltime;
}
out: out:
local_irq_restore(flags); local_irq_restore(flags);
...@@ -3338,11 +3360,11 @@ void unregister_ftrace_graph(void) ...@@ -3338,11 +3360,11 @@ void unregister_ftrace_graph(void)
goto out; goto out;
ftrace_graph_active--; ftrace_graph_active--;
unregister_trace_sched_switch(ftrace_graph_probe_sched_switch);
ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
ftrace_graph_entry = ftrace_graph_entry_stub; ftrace_graph_entry = ftrace_graph_entry_stub;
ftrace_shutdown(FTRACE_STOP_FUNC_RET); ftrace_shutdown(FTRACE_STOP_FUNC_RET);
unregister_pm_notifier(&ftrace_suspend_notifier); unregister_pm_notifier(&ftrace_suspend_notifier);
unregister_trace_sched_switch(ftrace_graph_probe_sched_switch);
out: out:
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
......
...@@ -2000,17 +2000,13 @@ rb_add_time_stamp(struct ring_buffer_per_cpu *cpu_buffer, ...@@ -2000,17 +2000,13 @@ rb_add_time_stamp(struct ring_buffer_per_cpu *cpu_buffer,
u64 *ts, u64 *delta) u64 *ts, u64 *delta)
{ {
struct ring_buffer_event *event; struct ring_buffer_event *event;
static int once;
int ret; int ret;
if (unlikely(*delta > (1ULL << 59) && !once++)) { WARN_ONCE(*delta > (1ULL << 59),
printk(KERN_WARNING "Delta way too big! %llu" KERN_WARNING "Delta way too big! %llu ts=%llu write stamp = %llu\n",
" ts=%llu write stamp = %llu\n", (unsigned long long)*delta,
(unsigned long long)*delta, (unsigned long long)*ts,
(unsigned long long)*ts, (unsigned long long)cpu_buffer->write_stamp);
(unsigned long long)cpu_buffer->write_stamp);
WARN_ON(1);
}
/* /*
* The delta is too big, we to add a * The delta is too big, we to add a
...@@ -3332,23 +3328,30 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts, ...@@ -3332,23 +3328,30 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts,
EXPORT_SYMBOL_GPL(ring_buffer_consume); EXPORT_SYMBOL_GPL(ring_buffer_consume);
/** /**
* ring_buffer_read_start - start a non consuming read of the buffer * ring_buffer_read_prepare - Prepare for a non consuming read of the buffer
* @buffer: The ring buffer to read from * @buffer: The ring buffer to read from
* @cpu: The cpu buffer to iterate over * @cpu: The cpu buffer to iterate over
* *
* This starts up an iteration through the buffer. It also disables * This performs the initial preparations necessary to iterate
* the recording to the buffer until the reading is finished. * through the buffer. Memory is allocated, buffer recording
* This prevents the reading from being corrupted. This is not * is disabled, and the iterator pointer is returned to the caller.
* a consuming read, so a producer is not expected.
* *
* Must be paired with ring_buffer_finish. * Disabling buffer recordng prevents the reading from being
* corrupted. This is not a consuming read, so a producer is not
* expected.
*
* After a sequence of ring_buffer_read_prepare calls, the user is
* expected to make at least one call to ring_buffer_prepare_sync.
* Afterwards, ring_buffer_read_start is invoked to get things going
* for real.
*
* This overall must be paired with ring_buffer_finish.
*/ */
struct ring_buffer_iter * struct ring_buffer_iter *
ring_buffer_read_start(struct ring_buffer *buffer, int cpu) ring_buffer_read_prepare(struct ring_buffer *buffer, int cpu)
{ {
struct ring_buffer_per_cpu *cpu_buffer; struct ring_buffer_per_cpu *cpu_buffer;
struct ring_buffer_iter *iter; struct ring_buffer_iter *iter;
unsigned long flags;
if (!cpumask_test_cpu(cpu, buffer->cpumask)) if (!cpumask_test_cpu(cpu, buffer->cpumask))
return NULL; return NULL;
...@@ -3362,15 +3365,52 @@ ring_buffer_read_start(struct ring_buffer *buffer, int cpu) ...@@ -3362,15 +3365,52 @@ ring_buffer_read_start(struct ring_buffer *buffer, int cpu)
iter->cpu_buffer = cpu_buffer; iter->cpu_buffer = cpu_buffer;
atomic_inc(&cpu_buffer->record_disabled); atomic_inc(&cpu_buffer->record_disabled);
return iter;
}
EXPORT_SYMBOL_GPL(ring_buffer_read_prepare);
/**
* ring_buffer_read_prepare_sync - Synchronize a set of prepare calls
*
* All previously invoked ring_buffer_read_prepare calls to prepare
* iterators will be synchronized. Afterwards, read_buffer_read_start
* calls on those iterators are allowed.
*/
void
ring_buffer_read_prepare_sync(void)
{
synchronize_sched(); synchronize_sched();
}
EXPORT_SYMBOL_GPL(ring_buffer_read_prepare_sync);
/**
* ring_buffer_read_start - start a non consuming read of the buffer
* @iter: The iterator returned by ring_buffer_read_prepare
*
* This finalizes the startup of an iteration through the buffer.
* The iterator comes from a call to ring_buffer_read_prepare and
* an intervening ring_buffer_read_prepare_sync must have been
* performed.
*
* Must be paired with ring_buffer_finish.
*/
void
ring_buffer_read_start(struct ring_buffer_iter *iter)
{
struct ring_buffer_per_cpu *cpu_buffer;
unsigned long flags;
if (!iter)
return;
cpu_buffer = iter->cpu_buffer;
spin_lock_irqsave(&cpu_buffer->reader_lock, flags); spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
arch_spin_lock(&cpu_buffer->lock); arch_spin_lock(&cpu_buffer->lock);
rb_iter_reset(iter); rb_iter_reset(iter);
arch_spin_unlock(&cpu_buffer->lock); arch_spin_unlock(&cpu_buffer->lock);
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
return iter;
} }
EXPORT_SYMBOL_GPL(ring_buffer_read_start); EXPORT_SYMBOL_GPL(ring_buffer_read_start);
......
...@@ -113,7 +113,8 @@ static enum event_status read_page(int cpu) ...@@ -113,7 +113,8 @@ static enum event_status read_page(int cpu)
ret = ring_buffer_read_page(buffer, &bpage, PAGE_SIZE, cpu, 1); ret = ring_buffer_read_page(buffer, &bpage, PAGE_SIZE, cpu, 1);
if (ret >= 0) { if (ret >= 0) {
rpage = bpage; rpage = bpage;
commit = local_read(&rpage->commit); /* The commit may have missed event flags set, clear them */
commit = local_read(&rpage->commit) & 0xfffff;
for (i = 0; i < commit && !kill_test; i += inc) { for (i = 0; i < commit && !kill_test; i += inc) {
if (i >= (PAGE_SIZE - offsetof(struct rb_page, data))) { if (i >= (PAGE_SIZE - offsetof(struct rb_page, data))) {
......
...@@ -117,9 +117,12 @@ static cpumask_var_t __read_mostly tracing_buffer_mask; ...@@ -117,9 +117,12 @@ static cpumask_var_t __read_mostly tracing_buffer_mask;
* *
* It is default off, but you can enable it with either specifying * It is default off, but you can enable it with either specifying
* "ftrace_dump_on_oops" in the kernel command line, or setting * "ftrace_dump_on_oops" in the kernel command line, or setting
* /proc/sys/kernel/ftrace_dump_on_oops to true. * /proc/sys/kernel/ftrace_dump_on_oops
* Set 1 if you want to dump buffers of all CPUs
* Set 2 if you want to dump the buffer of the CPU that triggered oops
*/ */
int ftrace_dump_on_oops;
enum ftrace_dump_mode ftrace_dump_on_oops;
static int tracing_set_tracer(const char *buf); static int tracing_set_tracer(const char *buf);
...@@ -139,8 +142,17 @@ __setup("ftrace=", set_cmdline_ftrace); ...@@ -139,8 +142,17 @@ __setup("ftrace=", set_cmdline_ftrace);
static int __init set_ftrace_dump_on_oops(char *str) static int __init set_ftrace_dump_on_oops(char *str)
{ {
ftrace_dump_on_oops = 1; if (*str++ != '=' || !*str) {
return 1; ftrace_dump_on_oops = DUMP_ALL;
return 1;
}
if (!strcmp("orig_cpu", str)) {
ftrace_dump_on_oops = DUMP_ORIG;
return 1;
}
return 0;
} }
__setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops);
...@@ -1571,7 +1583,7 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu, ...@@ -1571,7 +1583,7 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu,
{ {
struct ring_buffer *buffer = iter->tr->buffer; struct ring_buffer *buffer = iter->tr->buffer;
struct trace_entry *ent, *next = NULL; struct trace_entry *ent, *next = NULL;
unsigned long lost_events, next_lost = 0; unsigned long lost_events = 0, next_lost = 0;
int cpu_file = iter->cpu_file; int cpu_file = iter->cpu_file;
u64 next_ts = 0, ts; u64 next_ts = 0, ts;
int next_cpu = -1; int next_cpu = -1;
...@@ -1796,7 +1808,7 @@ static void print_func_help_header(struct seq_file *m) ...@@ -1796,7 +1808,7 @@ static void print_func_help_header(struct seq_file *m)
} }
static void void
print_trace_header(struct seq_file *m, struct trace_iterator *iter) print_trace_header(struct seq_file *m, struct trace_iterator *iter)
{ {
unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
...@@ -2005,7 +2017,7 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter) ...@@ -2005,7 +2017,7 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
return event ? event->binary(iter, 0) : TRACE_TYPE_HANDLED; return event ? event->binary(iter, 0) : TRACE_TYPE_HANDLED;
} }
static int trace_empty(struct trace_iterator *iter) int trace_empty(struct trace_iterator *iter)
{ {
int cpu; int cpu;
...@@ -2072,6 +2084,23 @@ static enum print_line_t print_trace_line(struct trace_iterator *iter) ...@@ -2072,6 +2084,23 @@ static enum print_line_t print_trace_line(struct trace_iterator *iter)
return print_trace_fmt(iter); return print_trace_fmt(iter);
} }
void trace_default_header(struct seq_file *m)
{
struct trace_iterator *iter = m->private;
if (iter->iter_flags & TRACE_FILE_LAT_FMT) {
/* print nothing if the buffers are empty */
if (trace_empty(iter))
return;
print_trace_header(m, iter);
if (!(trace_flags & TRACE_ITER_VERBOSE))
print_lat_help_header(m);
} else {
if (!(trace_flags & TRACE_ITER_VERBOSE))
print_func_help_header(m);
}
}
static int s_show(struct seq_file *m, void *v) static int s_show(struct seq_file *m, void *v)
{ {
struct trace_iterator *iter = v; struct trace_iterator *iter = v;
...@@ -2084,17 +2113,9 @@ static int s_show(struct seq_file *m, void *v) ...@@ -2084,17 +2113,9 @@ static int s_show(struct seq_file *m, void *v)
} }
if (iter->trace && iter->trace->print_header) if (iter->trace && iter->trace->print_header)
iter->trace->print_header(m); iter->trace->print_header(m);
else if (iter->iter_flags & TRACE_FILE_LAT_FMT) { else
/* print nothing if the buffers are empty */ trace_default_header(m);
if (trace_empty(iter))
return 0;
print_trace_header(m, iter);
if (!(trace_flags & TRACE_ITER_VERBOSE))
print_lat_help_header(m);
} else {
if (!(trace_flags & TRACE_ITER_VERBOSE))
print_func_help_header(m);
}
} else if (iter->leftover) { } else if (iter->leftover) {
/* /*
* If we filled the seq_file buffer earlier, we * If we filled the seq_file buffer earlier, we
...@@ -2180,15 +2201,20 @@ __tracing_open(struct inode *inode, struct file *file) ...@@ -2180,15 +2201,20 @@ __tracing_open(struct inode *inode, struct file *file)
if (iter->cpu_file == TRACE_PIPE_ALL_CPU) { if (iter->cpu_file == TRACE_PIPE_ALL_CPU) {
for_each_tracing_cpu(cpu) { for_each_tracing_cpu(cpu) {
iter->buffer_iter[cpu] = iter->buffer_iter[cpu] =
ring_buffer_read_start(iter->tr->buffer, cpu); ring_buffer_read_prepare(iter->tr->buffer, cpu);
}
ring_buffer_read_prepare_sync();
for_each_tracing_cpu(cpu) {
ring_buffer_read_start(iter->buffer_iter[cpu]);
tracing_iter_reset(iter, cpu); tracing_iter_reset(iter, cpu);
} }
} else { } else {
cpu = iter->cpu_file; cpu = iter->cpu_file;
iter->buffer_iter[cpu] = iter->buffer_iter[cpu] =
ring_buffer_read_start(iter->tr->buffer, cpu); ring_buffer_read_prepare(iter->tr->buffer, cpu);
ring_buffer_read_prepare_sync();
ring_buffer_read_start(iter->buffer_iter[cpu]);
tracing_iter_reset(iter, cpu); tracing_iter_reset(iter, cpu);
} }
...@@ -4338,7 +4364,7 @@ static int trace_panic_handler(struct notifier_block *this, ...@@ -4338,7 +4364,7 @@ static int trace_panic_handler(struct notifier_block *this,
unsigned long event, void *unused) unsigned long event, void *unused)
{ {
if (ftrace_dump_on_oops) if (ftrace_dump_on_oops)
ftrace_dump(); ftrace_dump(ftrace_dump_on_oops);
return NOTIFY_OK; return NOTIFY_OK;
} }
...@@ -4355,7 +4381,7 @@ static int trace_die_handler(struct notifier_block *self, ...@@ -4355,7 +4381,7 @@ static int trace_die_handler(struct notifier_block *self,
switch (val) { switch (val) {
case DIE_OOPS: case DIE_OOPS:
if (ftrace_dump_on_oops) if (ftrace_dump_on_oops)
ftrace_dump(); ftrace_dump(ftrace_dump_on_oops);
break; break;
default: default:
break; break;
...@@ -4396,7 +4422,8 @@ trace_printk_seq(struct trace_seq *s) ...@@ -4396,7 +4422,8 @@ trace_printk_seq(struct trace_seq *s)
trace_seq_init(s); trace_seq_init(s);
} }
static void __ftrace_dump(bool disable_tracing) static void
__ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
{ {
static arch_spinlock_t ftrace_dump_lock = static arch_spinlock_t ftrace_dump_lock =
(arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
...@@ -4429,12 +4456,25 @@ static void __ftrace_dump(bool disable_tracing) ...@@ -4429,12 +4456,25 @@ static void __ftrace_dump(bool disable_tracing)
/* don't look at user memory in panic mode */ /* don't look at user memory in panic mode */
trace_flags &= ~TRACE_ITER_SYM_USEROBJ; trace_flags &= ~TRACE_ITER_SYM_USEROBJ;
printk(KERN_TRACE "Dumping ftrace buffer:\n");
/* Simulate the iterator */ /* Simulate the iterator */
iter.tr = &global_trace; iter.tr = &global_trace;
iter.trace = current_trace; iter.trace = current_trace;
iter.cpu_file = TRACE_PIPE_ALL_CPU;
switch (oops_dump_mode) {
case DUMP_ALL:
iter.cpu_file = TRACE_PIPE_ALL_CPU;
break;
case DUMP_ORIG:
iter.cpu_file = raw_smp_processor_id();
break;
case DUMP_NONE:
goto out_enable;
default:
printk(KERN_TRACE "Bad dumping mode, switching to all CPUs dump\n");
iter.cpu_file = TRACE_PIPE_ALL_CPU;
}
printk(KERN_TRACE "Dumping ftrace buffer:\n");
/* /*
* We need to stop all tracing on all CPUS to read the * We need to stop all tracing on all CPUS to read the
...@@ -4473,6 +4513,7 @@ static void __ftrace_dump(bool disable_tracing) ...@@ -4473,6 +4513,7 @@ static void __ftrace_dump(bool disable_tracing)
else else
printk(KERN_TRACE "---------------------------------\n"); printk(KERN_TRACE "---------------------------------\n");
out_enable:
/* Re-enable tracing if requested */ /* Re-enable tracing if requested */
if (!disable_tracing) { if (!disable_tracing) {
trace_flags |= old_userobj; trace_flags |= old_userobj;
...@@ -4489,9 +4530,9 @@ static void __ftrace_dump(bool disable_tracing) ...@@ -4489,9 +4530,9 @@ static void __ftrace_dump(bool disable_tracing)
} }
/* By default: disable tracing after the dump */ /* By default: disable tracing after the dump */
void ftrace_dump(void) void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
{ {
__ftrace_dump(true); __ftrace_dump(true, oops_dump_mode);
} }
__init static int tracer_alloc_buffers(void) __init static int tracer_alloc_buffers(void)
......
...@@ -364,6 +364,9 @@ void trace_function(struct trace_array *tr, ...@@ -364,6 +364,9 @@ void trace_function(struct trace_array *tr,
unsigned long ip, unsigned long ip,
unsigned long parent_ip, unsigned long parent_ip,
unsigned long flags, int pc); unsigned long flags, int pc);
void trace_default_header(struct seq_file *m);
void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
int trace_empty(struct trace_iterator *iter);
void trace_graph_return(struct ftrace_graph_ret *trace); void trace_graph_return(struct ftrace_graph_ret *trace);
int trace_graph_entry(struct ftrace_graph_ent *trace); int trace_graph_entry(struct ftrace_graph_ent *trace);
...@@ -475,9 +478,29 @@ extern int trace_clock_id; ...@@ -475,9 +478,29 @@ extern int trace_clock_id;
/* Standard output formatting function used for function return traces */ /* Standard output formatting function used for function return traces */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
extern enum print_line_t print_graph_function(struct trace_iterator *iter);
/* Flag options */
#define TRACE_GRAPH_PRINT_OVERRUN 0x1
#define TRACE_GRAPH_PRINT_CPU 0x2
#define TRACE_GRAPH_PRINT_OVERHEAD 0x4
#define TRACE_GRAPH_PRINT_PROC 0x8
#define TRACE_GRAPH_PRINT_DURATION 0x10
#define TRACE_GRAPH_PRINT_ABS_TIME 0x20
extern enum print_line_t
print_graph_function_flags(struct trace_iterator *iter, u32 flags);
extern void print_graph_headers_flags(struct seq_file *s, u32 flags);
extern enum print_line_t extern enum print_line_t
trace_print_graph_duration(unsigned long long duration, struct trace_seq *s); trace_print_graph_duration(unsigned long long duration, struct trace_seq *s);
extern void graph_trace_open(struct trace_iterator *iter);
extern void graph_trace_close(struct trace_iterator *iter);
extern int __trace_graph_entry(struct trace_array *tr,
struct ftrace_graph_ent *trace,
unsigned long flags, int pc);
extern void __trace_graph_return(struct trace_array *tr,
struct ftrace_graph_ret *trace,
unsigned long flags, int pc);
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
/* TODO: make this variable */ /* TODO: make this variable */
...@@ -508,7 +531,7 @@ static inline int ftrace_graph_addr(unsigned long addr) ...@@ -508,7 +531,7 @@ static inline int ftrace_graph_addr(unsigned long addr)
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
#else /* CONFIG_FUNCTION_GRAPH_TRACER */ #else /* CONFIG_FUNCTION_GRAPH_TRACER */
static inline enum print_line_t static inline enum print_line_t
print_graph_function(struct trace_iterator *iter) print_graph_function_flags(struct trace_iterator *iter, u32 flags)
{ {
return TRACE_TYPE_UNHANDLED; return TRACE_TYPE_UNHANDLED;
} }
......
...@@ -40,7 +40,7 @@ struct fgraph_data { ...@@ -40,7 +40,7 @@ struct fgraph_data {
#define TRACE_GRAPH_PRINT_OVERHEAD 0x4 #define TRACE_GRAPH_PRINT_OVERHEAD 0x4
#define TRACE_GRAPH_PRINT_PROC 0x8 #define TRACE_GRAPH_PRINT_PROC 0x8
#define TRACE_GRAPH_PRINT_DURATION 0x10 #define TRACE_GRAPH_PRINT_DURATION 0x10
#define TRACE_GRAPH_PRINT_ABS_TIME 0X20 #define TRACE_GRAPH_PRINT_ABS_TIME 0x20
static struct tracer_opt trace_opts[] = { static struct tracer_opt trace_opts[] = {
/* Display overruns? (for self-debug purpose) */ /* Display overruns? (for self-debug purpose) */
...@@ -179,7 +179,7 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer) ...@@ -179,7 +179,7 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
return ret; return ret;
} }
static int __trace_graph_entry(struct trace_array *tr, int __trace_graph_entry(struct trace_array *tr,
struct ftrace_graph_ent *trace, struct ftrace_graph_ent *trace,
unsigned long flags, unsigned long flags,
int pc) int pc)
...@@ -246,7 +246,7 @@ int trace_graph_thresh_entry(struct ftrace_graph_ent *trace) ...@@ -246,7 +246,7 @@ int trace_graph_thresh_entry(struct ftrace_graph_ent *trace)
return trace_graph_entry(trace); return trace_graph_entry(trace);
} }
static void __trace_graph_return(struct trace_array *tr, void __trace_graph_return(struct trace_array *tr,
struct ftrace_graph_ret *trace, struct ftrace_graph_ret *trace,
unsigned long flags, unsigned long flags,
int pc) int pc)
...@@ -527,17 +527,18 @@ get_return_for_leaf(struct trace_iterator *iter, ...@@ -527,17 +527,18 @@ get_return_for_leaf(struct trace_iterator *iter,
/* Signal a overhead of time execution to the output */ /* Signal a overhead of time execution to the output */
static int static int
print_graph_overhead(unsigned long long duration, struct trace_seq *s) print_graph_overhead(unsigned long long duration, struct trace_seq *s,
u32 flags)
{ {
/* If duration disappear, we don't need anything */ /* If duration disappear, we don't need anything */
if (!(tracer_flags.val & TRACE_GRAPH_PRINT_DURATION)) if (!(flags & TRACE_GRAPH_PRINT_DURATION))
return 1; return 1;
/* Non nested entry or return */ /* Non nested entry or return */
if (duration == -1) if (duration == -1)
return trace_seq_printf(s, " "); return trace_seq_printf(s, " ");
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { if (flags & TRACE_GRAPH_PRINT_OVERHEAD) {
/* Duration exceeded 100 msecs */ /* Duration exceeded 100 msecs */
if (duration > 100000ULL) if (duration > 100000ULL)
return trace_seq_printf(s, "! "); return trace_seq_printf(s, "! ");
...@@ -563,7 +564,7 @@ static int print_graph_abs_time(u64 t, struct trace_seq *s) ...@@ -563,7 +564,7 @@ static int print_graph_abs_time(u64 t, struct trace_seq *s)
static enum print_line_t static enum print_line_t
print_graph_irq(struct trace_iterator *iter, unsigned long addr, print_graph_irq(struct trace_iterator *iter, unsigned long addr,
enum trace_type type, int cpu, pid_t pid) enum trace_type type, int cpu, pid_t pid, u32 flags)
{ {
int ret; int ret;
struct trace_seq *s = &iter->seq; struct trace_seq *s = &iter->seq;
...@@ -573,21 +574,21 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr, ...@@ -573,21 +574,21 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
return TRACE_TYPE_UNHANDLED; return TRACE_TYPE_UNHANDLED;
/* Absolute time */ /* Absolute time */
if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { if (flags & TRACE_GRAPH_PRINT_ABS_TIME) {
ret = print_graph_abs_time(iter->ts, s); ret = print_graph_abs_time(iter->ts, s);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
/* Cpu */ /* Cpu */
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { if (flags & TRACE_GRAPH_PRINT_CPU) {
ret = print_graph_cpu(s, cpu); ret = print_graph_cpu(s, cpu);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
/* Proc */ /* Proc */
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { if (flags & TRACE_GRAPH_PRINT_PROC) {
ret = print_graph_proc(s, pid); ret = print_graph_proc(s, pid);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -597,7 +598,7 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr, ...@@ -597,7 +598,7 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
} }
/* No overhead */ /* No overhead */
ret = print_graph_overhead(-1, s); ret = print_graph_overhead(-1, s, flags);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -610,7 +611,7 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr, ...@@ -610,7 +611,7 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Don't close the duration column if haven't one */ /* Don't close the duration column if haven't one */
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) if (flags & TRACE_GRAPH_PRINT_DURATION)
trace_seq_printf(s, " |"); trace_seq_printf(s, " |");
ret = trace_seq_printf(s, "\n"); ret = trace_seq_printf(s, "\n");
...@@ -680,7 +681,8 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s) ...@@ -680,7 +681,8 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s)
static enum print_line_t static enum print_line_t
print_graph_entry_leaf(struct trace_iterator *iter, print_graph_entry_leaf(struct trace_iterator *iter,
struct ftrace_graph_ent_entry *entry, struct ftrace_graph_ent_entry *entry,
struct ftrace_graph_ret_entry *ret_entry, struct trace_seq *s) struct ftrace_graph_ret_entry *ret_entry,
struct trace_seq *s, u32 flags)
{ {
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
struct ftrace_graph_ret *graph_ret; struct ftrace_graph_ret *graph_ret;
...@@ -712,12 +714,12 @@ print_graph_entry_leaf(struct trace_iterator *iter, ...@@ -712,12 +714,12 @@ print_graph_entry_leaf(struct trace_iterator *iter,
} }
/* Overhead */ /* Overhead */
ret = print_graph_overhead(duration, s); ret = print_graph_overhead(duration, s, flags);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Duration */ /* Duration */
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { if (flags & TRACE_GRAPH_PRINT_DURATION) {
ret = print_graph_duration(duration, s); ret = print_graph_duration(duration, s);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -740,7 +742,7 @@ print_graph_entry_leaf(struct trace_iterator *iter, ...@@ -740,7 +742,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
static enum print_line_t static enum print_line_t
print_graph_entry_nested(struct trace_iterator *iter, print_graph_entry_nested(struct trace_iterator *iter,
struct ftrace_graph_ent_entry *entry, struct ftrace_graph_ent_entry *entry,
struct trace_seq *s, int cpu) struct trace_seq *s, int cpu, u32 flags)
{ {
struct ftrace_graph_ent *call = &entry->graph_ent; struct ftrace_graph_ent *call = &entry->graph_ent;
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
...@@ -760,12 +762,12 @@ print_graph_entry_nested(struct trace_iterator *iter, ...@@ -760,12 +762,12 @@ print_graph_entry_nested(struct trace_iterator *iter,
} }
/* No overhead */ /* No overhead */
ret = print_graph_overhead(-1, s); ret = print_graph_overhead(-1, s, flags);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* No time */ /* No time */
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { if (flags & TRACE_GRAPH_PRINT_DURATION) {
ret = trace_seq_printf(s, " | "); ret = trace_seq_printf(s, " | ");
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -791,7 +793,7 @@ print_graph_entry_nested(struct trace_iterator *iter, ...@@ -791,7 +793,7 @@ print_graph_entry_nested(struct trace_iterator *iter,
static enum print_line_t static enum print_line_t
print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s,
int type, unsigned long addr) int type, unsigned long addr, u32 flags)
{ {
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
struct trace_entry *ent = iter->ent; struct trace_entry *ent = iter->ent;
...@@ -804,27 +806,27 @@ print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, ...@@ -804,27 +806,27 @@ print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s,
if (type) { if (type) {
/* Interrupt */ /* Interrupt */
ret = print_graph_irq(iter, addr, type, cpu, ent->pid); ret = print_graph_irq(iter, addr, type, cpu, ent->pid, flags);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
/* Absolute time */ /* Absolute time */
if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { if (flags & TRACE_GRAPH_PRINT_ABS_TIME) {
ret = print_graph_abs_time(iter->ts, s); ret = print_graph_abs_time(iter->ts, s);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
/* Cpu */ /* Cpu */
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { if (flags & TRACE_GRAPH_PRINT_CPU) {
ret = print_graph_cpu(s, cpu); ret = print_graph_cpu(s, cpu);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
/* Proc */ /* Proc */
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { if (flags & TRACE_GRAPH_PRINT_PROC) {
ret = print_graph_proc(s, ent->pid); ret = print_graph_proc(s, ent->pid);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -846,7 +848,7 @@ print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, ...@@ -846,7 +848,7 @@ print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s,
static enum print_line_t static enum print_line_t
print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
struct trace_iterator *iter) struct trace_iterator *iter, u32 flags)
{ {
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
struct ftrace_graph_ent *call = &field->graph_ent; struct ftrace_graph_ent *call = &field->graph_ent;
...@@ -854,14 +856,14 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, ...@@ -854,14 +856,14 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
static enum print_line_t ret; static enum print_line_t ret;
int cpu = iter->cpu; int cpu = iter->cpu;
if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func)) if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func, flags))
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
leaf_ret = get_return_for_leaf(iter, field); leaf_ret = get_return_for_leaf(iter, field);
if (leaf_ret) if (leaf_ret)
ret = print_graph_entry_leaf(iter, field, leaf_ret, s); ret = print_graph_entry_leaf(iter, field, leaf_ret, s, flags);
else else
ret = print_graph_entry_nested(iter, field, s, cpu); ret = print_graph_entry_nested(iter, field, s, cpu, flags);
if (data) { if (data) {
/* /*
...@@ -880,7 +882,8 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, ...@@ -880,7 +882,8 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
static enum print_line_t static enum print_line_t
print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
struct trace_entry *ent, struct trace_iterator *iter) struct trace_entry *ent, struct trace_iterator *iter,
u32 flags)
{ {
unsigned long long duration = trace->rettime - trace->calltime; unsigned long long duration = trace->rettime - trace->calltime;
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
...@@ -910,16 +913,16 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, ...@@ -910,16 +913,16 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
} }
} }
if (print_graph_prologue(iter, s, 0, 0)) if (print_graph_prologue(iter, s, 0, 0, flags))
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Overhead */ /* Overhead */
ret = print_graph_overhead(duration, s); ret = print_graph_overhead(duration, s, flags);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Duration */ /* Duration */
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { if (flags & TRACE_GRAPH_PRINT_DURATION) {
ret = print_graph_duration(duration, s); ret = print_graph_duration(duration, s);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -949,14 +952,15 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, ...@@ -949,14 +952,15 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
} }
/* Overrun */ /* Overrun */
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { if (flags & TRACE_GRAPH_PRINT_OVERRUN) {
ret = trace_seq_printf(s, " (Overruns: %lu)\n", ret = trace_seq_printf(s, " (Overruns: %lu)\n",
trace->overrun); trace->overrun);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
ret = print_graph_irq(iter, trace->func, TRACE_GRAPH_RET, cpu, pid); ret = print_graph_irq(iter, trace->func, TRACE_GRAPH_RET,
cpu, pid, flags);
if (ret == TRACE_TYPE_PARTIAL_LINE) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -964,8 +968,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, ...@@ -964,8 +968,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
} }
static enum print_line_t static enum print_line_t
print_graph_comment(struct trace_seq *s, struct trace_entry *ent, print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
struct trace_iterator *iter) struct trace_iterator *iter, u32 flags)
{ {
unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
...@@ -977,16 +981,16 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, ...@@ -977,16 +981,16 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
if (data) if (data)
depth = per_cpu_ptr(data->cpu_data, iter->cpu)->depth; depth = per_cpu_ptr(data->cpu_data, iter->cpu)->depth;
if (print_graph_prologue(iter, s, 0, 0)) if (print_graph_prologue(iter, s, 0, 0, flags))
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* No overhead */ /* No overhead */
ret = print_graph_overhead(-1, s); ret = print_graph_overhead(-1, s, flags);
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* No time */ /* No time */
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { if (flags & TRACE_GRAPH_PRINT_DURATION) {
ret = trace_seq_printf(s, " | "); ret = trace_seq_printf(s, " | ");
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
...@@ -1041,7 +1045,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, ...@@ -1041,7 +1045,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
enum print_line_t enum print_line_t
print_graph_function(struct trace_iterator *iter) print_graph_function_flags(struct trace_iterator *iter, u32 flags)
{ {
struct ftrace_graph_ent_entry *field; struct ftrace_graph_ent_entry *field;
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
...@@ -1062,7 +1066,7 @@ print_graph_function(struct trace_iterator *iter) ...@@ -1062,7 +1066,7 @@ print_graph_function(struct trace_iterator *iter)
if (data && data->failed) { if (data && data->failed) {
field = &data->ent; field = &data->ent;
iter->cpu = data->cpu; iter->cpu = data->cpu;
ret = print_graph_entry(field, s, iter); ret = print_graph_entry(field, s, iter, flags);
if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) { if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) {
per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1; per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1;
ret = TRACE_TYPE_NO_CONSUME; ret = TRACE_TYPE_NO_CONSUME;
...@@ -1082,32 +1086,49 @@ print_graph_function(struct trace_iterator *iter) ...@@ -1082,32 +1086,49 @@ print_graph_function(struct trace_iterator *iter)
struct ftrace_graph_ent_entry saved; struct ftrace_graph_ent_entry saved;
trace_assign_type(field, entry); trace_assign_type(field, entry);
saved = *field; saved = *field;
return print_graph_entry(&saved, s, iter); return print_graph_entry(&saved, s, iter, flags);
} }
case TRACE_GRAPH_RET: { case TRACE_GRAPH_RET: {
struct ftrace_graph_ret_entry *field; struct ftrace_graph_ret_entry *field;
trace_assign_type(field, entry); trace_assign_type(field, entry);
return print_graph_return(&field->ret, s, entry, iter); return print_graph_return(&field->ret, s, entry, iter, flags);
} }
case TRACE_STACK:
case TRACE_FN:
/* dont trace stack and functions as comments */
return TRACE_TYPE_UNHANDLED;
default: default:
return print_graph_comment(s, entry, iter); return print_graph_comment(s, entry, iter, flags);
} }
return TRACE_TYPE_HANDLED; return TRACE_TYPE_HANDLED;
} }
static void print_lat_header(struct seq_file *s) static enum print_line_t
print_graph_function(struct trace_iterator *iter)
{
return print_graph_function_flags(iter, tracer_flags.val);
}
static enum print_line_t
print_graph_function_event(struct trace_iterator *iter, int flags)
{
return print_graph_function(iter);
}
static void print_lat_header(struct seq_file *s, u32 flags)
{ {
static const char spaces[] = " " /* 16 spaces */ static const char spaces[] = " " /* 16 spaces */
" " /* 4 spaces */ " " /* 4 spaces */
" "; /* 17 spaces */ " "; /* 17 spaces */
int size = 0; int size = 0;
if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
size += 16; size += 16;
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) if (flags & TRACE_GRAPH_PRINT_CPU)
size += 4; size += 4;
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) if (flags & TRACE_GRAPH_PRINT_PROC)
size += 17; size += 17;
seq_printf(s, "#%.*s _-----=> irqs-off \n", size, spaces); seq_printf(s, "#%.*s _-----=> irqs-off \n", size, spaces);
...@@ -1118,43 +1139,48 @@ static void print_lat_header(struct seq_file *s) ...@@ -1118,43 +1139,48 @@ static void print_lat_header(struct seq_file *s)
seq_printf(s, "#%.*s|||| / \n", size, spaces); seq_printf(s, "#%.*s|||| / \n", size, spaces);
} }
static void print_graph_headers(struct seq_file *s) void print_graph_headers_flags(struct seq_file *s, u32 flags)
{ {
int lat = trace_flags & TRACE_ITER_LATENCY_FMT; int lat = trace_flags & TRACE_ITER_LATENCY_FMT;
if (lat) if (lat)
print_lat_header(s); print_lat_header(s, flags);
/* 1st line */ /* 1st line */
seq_printf(s, "#"); seq_printf(s, "#");
if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
seq_printf(s, " TIME "); seq_printf(s, " TIME ");
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) if (flags & TRACE_GRAPH_PRINT_CPU)
seq_printf(s, " CPU"); seq_printf(s, " CPU");
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) if (flags & TRACE_GRAPH_PRINT_PROC)
seq_printf(s, " TASK/PID "); seq_printf(s, " TASK/PID ");
if (lat) if (lat)
seq_printf(s, "|||||"); seq_printf(s, "|||||");
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) if (flags & TRACE_GRAPH_PRINT_DURATION)
seq_printf(s, " DURATION "); seq_printf(s, " DURATION ");
seq_printf(s, " FUNCTION CALLS\n"); seq_printf(s, " FUNCTION CALLS\n");
/* 2nd line */ /* 2nd line */
seq_printf(s, "#"); seq_printf(s, "#");
if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
seq_printf(s, " | "); seq_printf(s, " | ");
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) if (flags & TRACE_GRAPH_PRINT_CPU)
seq_printf(s, " | "); seq_printf(s, " | ");
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) if (flags & TRACE_GRAPH_PRINT_PROC)
seq_printf(s, " | | "); seq_printf(s, " | | ");
if (lat) if (lat)
seq_printf(s, "|||||"); seq_printf(s, "|||||");
if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) if (flags & TRACE_GRAPH_PRINT_DURATION)
seq_printf(s, " | | "); seq_printf(s, " | | ");
seq_printf(s, " | | | |\n"); seq_printf(s, " | | | |\n");
} }
static void graph_trace_open(struct trace_iterator *iter) void print_graph_headers(struct seq_file *s)
{
print_graph_headers_flags(s, tracer_flags.val);
}
void graph_trace_open(struct trace_iterator *iter)
{ {
/* pid and depth on the last trace processed */ /* pid and depth on the last trace processed */
struct fgraph_data *data; struct fgraph_data *data;
...@@ -1189,7 +1215,7 @@ static void graph_trace_open(struct trace_iterator *iter) ...@@ -1189,7 +1215,7 @@ static void graph_trace_open(struct trace_iterator *iter)
pr_warning("function graph tracer: not enough memory\n"); pr_warning("function graph tracer: not enough memory\n");
} }
static void graph_trace_close(struct trace_iterator *iter) void graph_trace_close(struct trace_iterator *iter)
{ {
struct fgraph_data *data = iter->private; struct fgraph_data *data = iter->private;
...@@ -1199,6 +1225,16 @@ static void graph_trace_close(struct trace_iterator *iter) ...@@ -1199,6 +1225,16 @@ static void graph_trace_close(struct trace_iterator *iter)
} }
} }
static struct trace_event graph_trace_entry_event = {
.type = TRACE_GRAPH_ENT,
.trace = print_graph_function_event,
};
static struct trace_event graph_trace_ret_event = {
.type = TRACE_GRAPH_RET,
.trace = print_graph_function_event,
};
static struct tracer graph_trace __read_mostly = { static struct tracer graph_trace __read_mostly = {
.name = "function_graph", .name = "function_graph",
.open = graph_trace_open, .open = graph_trace_open,
...@@ -1220,6 +1256,16 @@ static __init int init_graph_trace(void) ...@@ -1220,6 +1256,16 @@ static __init int init_graph_trace(void)
{ {
max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1);
if (!register_ftrace_event(&graph_trace_entry_event)) {
pr_warning("Warning: could not register graph trace events\n");
return 1;
}
if (!register_ftrace_event(&graph_trace_ret_event)) {
pr_warning("Warning: could not register graph trace events\n");
return 1;
}
return register_tracer(&graph_trace); return register_tracer(&graph_trace);
} }
......
...@@ -34,6 +34,9 @@ static int trace_type __read_mostly; ...@@ -34,6 +34,9 @@ static int trace_type __read_mostly;
static int save_lat_flag; static int save_lat_flag;
static void stop_irqsoff_tracer(struct trace_array *tr, int graph);
static int start_irqsoff_tracer(struct trace_array *tr, int graph);
#ifdef CONFIG_PREEMPT_TRACER #ifdef CONFIG_PREEMPT_TRACER
static inline int static inline int
preempt_trace(void) preempt_trace(void)
...@@ -55,6 +58,23 @@ irq_trace(void) ...@@ -55,6 +58,23 @@ irq_trace(void)
# define irq_trace() (0) # define irq_trace() (0)
#endif #endif
#define TRACE_DISPLAY_GRAPH 1
static struct tracer_opt trace_opts[] = {
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/* display latency trace as call graph */
{ TRACER_OPT(display-graph, TRACE_DISPLAY_GRAPH) },
#endif
{ } /* Empty entry */
};
static struct tracer_flags tracer_flags = {
.val = 0,
.opts = trace_opts,
};
#define is_graph() (tracer_flags.val & TRACE_DISPLAY_GRAPH)
/* /*
* Sequence count - we record it when starting a measurement and * Sequence count - we record it when starting a measurement and
* skip the latency if the sequence has changed - some other section * skip the latency if the sequence has changed - some other section
...@@ -108,6 +128,202 @@ static struct ftrace_ops trace_ops __read_mostly = ...@@ -108,6 +128,202 @@ static struct ftrace_ops trace_ops __read_mostly =
}; };
#endif /* CONFIG_FUNCTION_TRACER */ #endif /* CONFIG_FUNCTION_TRACER */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static int irqsoff_set_flag(u32 old_flags, u32 bit, int set)
{
int cpu;
if (!(bit & TRACE_DISPLAY_GRAPH))
return -EINVAL;
if (!(is_graph() ^ set))
return 0;
stop_irqsoff_tracer(irqsoff_trace, !set);
for_each_possible_cpu(cpu)
per_cpu(tracing_cpu, cpu) = 0;
tracing_max_latency = 0;
tracing_reset_online_cpus(irqsoff_trace);
return start_irqsoff_tracer(irqsoff_trace, set);
}
static int irqsoff_graph_entry(struct ftrace_graph_ent *trace)
{
struct trace_array *tr = irqsoff_trace;
struct trace_array_cpu *data;
unsigned long flags;
long disabled;
int ret;
int cpu;
int pc;
cpu = raw_smp_processor_id();
if (likely(!per_cpu(tracing_cpu, cpu)))
return 0;
local_save_flags(flags);
/* slight chance to get a false positive on tracing_cpu */
if (!irqs_disabled_flags(flags))
return 0;
data = tr->data[cpu];
disabled = atomic_inc_return(&data->disabled);
if (likely(disabled == 1)) {
pc = preempt_count();
ret = __trace_graph_entry(tr, trace, flags, pc);
} else
ret = 0;
atomic_dec(&data->disabled);
return ret;
}
static void irqsoff_graph_return(struct ftrace_graph_ret *trace)
{
struct trace_array *tr = irqsoff_trace;
struct trace_array_cpu *data;
unsigned long flags;
long disabled;
int cpu;
int pc;
cpu = raw_smp_processor_id();
if (likely(!per_cpu(tracing_cpu, cpu)))
return;
local_save_flags(flags);
/* slight chance to get a false positive on tracing_cpu */
if (!irqs_disabled_flags(flags))
return;
data = tr->data[cpu];
disabled = atomic_inc_return(&data->disabled);
if (likely(disabled == 1)) {
pc = preempt_count();
__trace_graph_return(tr, trace, flags, pc);
}
atomic_dec(&data->disabled);
}
static void irqsoff_trace_open(struct trace_iterator *iter)
{
if (is_graph())
graph_trace_open(iter);
}
static void irqsoff_trace_close(struct trace_iterator *iter)
{
if (iter->private)
graph_trace_close(iter);
}
#define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_CPU | \
TRACE_GRAPH_PRINT_PROC)
static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
{
u32 flags = GRAPH_TRACER_FLAGS;
if (trace_flags & TRACE_ITER_LATENCY_FMT)
flags |= TRACE_GRAPH_PRINT_DURATION;
else
flags |= TRACE_GRAPH_PRINT_ABS_TIME;
/*
* In graph mode call the graph tracer output function,
* otherwise go with the TRACE_FN event handler
*/
if (is_graph())
return print_graph_function_flags(iter, flags);
return TRACE_TYPE_UNHANDLED;
}
static void irqsoff_print_header(struct seq_file *s)
{
if (is_graph()) {
struct trace_iterator *iter = s->private;
u32 flags = GRAPH_TRACER_FLAGS;
if (trace_flags & TRACE_ITER_LATENCY_FMT) {
/* print nothing if the buffers are empty */
if (trace_empty(iter))
return;
print_trace_header(s, iter);
flags |= TRACE_GRAPH_PRINT_DURATION;
} else
flags |= TRACE_GRAPH_PRINT_ABS_TIME;
print_graph_headers_flags(s, flags);
} else
trace_default_header(s);
}
static void
trace_graph_function(struct trace_array *tr,
unsigned long ip, unsigned long flags, int pc)
{
u64 time = trace_clock_local();
struct ftrace_graph_ent ent = {
.func = ip,
.depth = 0,
};
struct ftrace_graph_ret ret = {
.func = ip,
.depth = 0,
.calltime = time,
.rettime = time,
};
__trace_graph_entry(tr, &ent, flags, pc);
__trace_graph_return(tr, &ret, flags, pc);
}
static void
__trace_function(struct trace_array *tr,
unsigned long ip, unsigned long parent_ip,
unsigned long flags, int pc)
{
if (!is_graph())
trace_function(tr, ip, parent_ip, flags, pc);
else {
trace_graph_function(tr, parent_ip, flags, pc);
trace_graph_function(tr, ip, flags, pc);
}
}
#else
#define __trace_function trace_function
static int irqsoff_set_flag(u32 old_flags, u32 bit, int set)
{
return -EINVAL;
}
static int irqsoff_graph_entry(struct ftrace_graph_ent *trace)
{
return -1;
}
static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
{
return TRACE_TYPE_UNHANDLED;
}
static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { }
static void irqsoff_print_header(struct seq_file *s) { }
static void irqsoff_trace_open(struct trace_iterator *iter) { }
static void irqsoff_trace_close(struct trace_iterator *iter) { }
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
/* /*
* Should this new latency be reported/recorded? * Should this new latency be reported/recorded?
*/ */
...@@ -150,7 +366,7 @@ check_critical_timing(struct trace_array *tr, ...@@ -150,7 +366,7 @@ check_critical_timing(struct trace_array *tr,
if (!report_latency(delta)) if (!report_latency(delta))
goto out_unlock; goto out_unlock;
trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); __trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
/* Skip 5 functions to get to the irq/preempt enable function */ /* Skip 5 functions to get to the irq/preempt enable function */
__trace_stack(tr, flags, 5, pc); __trace_stack(tr, flags, 5, pc);
...@@ -172,7 +388,7 @@ check_critical_timing(struct trace_array *tr, ...@@ -172,7 +388,7 @@ check_critical_timing(struct trace_array *tr,
out: out:
data->critical_sequence = max_sequence; data->critical_sequence = max_sequence;
data->preempt_timestamp = ftrace_now(cpu); data->preempt_timestamp = ftrace_now(cpu);
trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc); __trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
} }
static inline void static inline void
...@@ -204,7 +420,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip) ...@@ -204,7 +420,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip)
local_save_flags(flags); local_save_flags(flags);
trace_function(tr, ip, parent_ip, flags, preempt_count()); __trace_function(tr, ip, parent_ip, flags, preempt_count());
per_cpu(tracing_cpu, cpu) = 1; per_cpu(tracing_cpu, cpu) = 1;
...@@ -238,7 +454,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) ...@@ -238,7 +454,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip)
atomic_inc(&data->disabled); atomic_inc(&data->disabled);
local_save_flags(flags); local_save_flags(flags);
trace_function(tr, ip, parent_ip, flags, preempt_count()); __trace_function(tr, ip, parent_ip, flags, preempt_count());
check_critical_timing(tr, data, parent_ip ? : ip, cpu); check_critical_timing(tr, data, parent_ip ? : ip, cpu);
data->critical_start = 0; data->critical_start = 0;
atomic_dec(&data->disabled); atomic_dec(&data->disabled);
...@@ -347,19 +563,32 @@ void trace_preempt_off(unsigned long a0, unsigned long a1) ...@@ -347,19 +563,32 @@ void trace_preempt_off(unsigned long a0, unsigned long a1)
} }
#endif /* CONFIG_PREEMPT_TRACER */ #endif /* CONFIG_PREEMPT_TRACER */
static void start_irqsoff_tracer(struct trace_array *tr) static int start_irqsoff_tracer(struct trace_array *tr, int graph)
{ {
register_ftrace_function(&trace_ops); int ret = 0;
if (tracing_is_enabled())
if (!graph)
ret = register_ftrace_function(&trace_ops);
else
ret = register_ftrace_graph(&irqsoff_graph_return,
&irqsoff_graph_entry);
if (!ret && tracing_is_enabled())
tracer_enabled = 1; tracer_enabled = 1;
else else
tracer_enabled = 0; tracer_enabled = 0;
return ret;
} }
static void stop_irqsoff_tracer(struct trace_array *tr) static void stop_irqsoff_tracer(struct trace_array *tr, int graph)
{ {
tracer_enabled = 0; tracer_enabled = 0;
unregister_ftrace_function(&trace_ops);
if (!graph)
unregister_ftrace_function(&trace_ops);
else
unregister_ftrace_graph();
} }
static void __irqsoff_tracer_init(struct trace_array *tr) static void __irqsoff_tracer_init(struct trace_array *tr)
...@@ -372,12 +601,14 @@ static void __irqsoff_tracer_init(struct trace_array *tr) ...@@ -372,12 +601,14 @@ static void __irqsoff_tracer_init(struct trace_array *tr)
/* make sure that the tracer is visible */ /* make sure that the tracer is visible */
smp_wmb(); smp_wmb();
tracing_reset_online_cpus(tr); tracing_reset_online_cpus(tr);
start_irqsoff_tracer(tr);
if (start_irqsoff_tracer(tr, is_graph()))
printk(KERN_ERR "failed to start irqsoff tracer\n");
} }
static void irqsoff_tracer_reset(struct trace_array *tr) static void irqsoff_tracer_reset(struct trace_array *tr)
{ {
stop_irqsoff_tracer(tr); stop_irqsoff_tracer(tr, is_graph());
if (!save_lat_flag) if (!save_lat_flag)
trace_flags &= ~TRACE_ITER_LATENCY_FMT; trace_flags &= ~TRACE_ITER_LATENCY_FMT;
...@@ -409,9 +640,15 @@ static struct tracer irqsoff_tracer __read_mostly = ...@@ -409,9 +640,15 @@ static struct tracer irqsoff_tracer __read_mostly =
.start = irqsoff_tracer_start, .start = irqsoff_tracer_start,
.stop = irqsoff_tracer_stop, .stop = irqsoff_tracer_stop,
.print_max = 1, .print_max = 1,
.print_header = irqsoff_print_header,
.print_line = irqsoff_print_line,
.flags = &tracer_flags,
.set_flag = irqsoff_set_flag,
#ifdef CONFIG_FTRACE_SELFTEST #ifdef CONFIG_FTRACE_SELFTEST
.selftest = trace_selftest_startup_irqsoff, .selftest = trace_selftest_startup_irqsoff,
#endif #endif
.open = irqsoff_trace_open,
.close = irqsoff_trace_close,
}; };
# define register_irqsoff(trace) register_tracer(&trace) # define register_irqsoff(trace) register_tracer(&trace)
#else #else
...@@ -435,9 +672,15 @@ static struct tracer preemptoff_tracer __read_mostly = ...@@ -435,9 +672,15 @@ static struct tracer preemptoff_tracer __read_mostly =
.start = irqsoff_tracer_start, .start = irqsoff_tracer_start,
.stop = irqsoff_tracer_stop, .stop = irqsoff_tracer_stop,
.print_max = 1, .print_max = 1,
.print_header = irqsoff_print_header,
.print_line = irqsoff_print_line,
.flags = &tracer_flags,
.set_flag = irqsoff_set_flag,
#ifdef CONFIG_FTRACE_SELFTEST #ifdef CONFIG_FTRACE_SELFTEST
.selftest = trace_selftest_startup_preemptoff, .selftest = trace_selftest_startup_preemptoff,
#endif #endif
.open = irqsoff_trace_open,
.close = irqsoff_trace_close,
}; };
# define register_preemptoff(trace) register_tracer(&trace) # define register_preemptoff(trace) register_tracer(&trace)
#else #else
...@@ -463,9 +706,15 @@ static struct tracer preemptirqsoff_tracer __read_mostly = ...@@ -463,9 +706,15 @@ static struct tracer preemptirqsoff_tracer __read_mostly =
.start = irqsoff_tracer_start, .start = irqsoff_tracer_start,
.stop = irqsoff_tracer_stop, .stop = irqsoff_tracer_stop,
.print_max = 1, .print_max = 1,
.print_header = irqsoff_print_header,
.print_line = irqsoff_print_line,
.flags = &tracer_flags,
.set_flag = irqsoff_set_flag,
#ifdef CONFIG_FTRACE_SELFTEST #ifdef CONFIG_FTRACE_SELFTEST
.selftest = trace_selftest_startup_preemptirqsoff, .selftest = trace_selftest_startup_preemptirqsoff,
#endif #endif
.open = irqsoff_trace_open,
.close = irqsoff_trace_close,
}; };
# define register_preemptirqsoff(trace) register_tracer(&trace) # define register_preemptirqsoff(trace) register_tracer(&trace)
......
...@@ -253,7 +253,7 @@ void *trace_seq_reserve(struct trace_seq *s, size_t len) ...@@ -253,7 +253,7 @@ void *trace_seq_reserve(struct trace_seq *s, size_t len)
void *ret; void *ret;
if (s->full) if (s->full)
return 0; return NULL;
if (len > ((PAGE_SIZE - 1) - s->len)) { if (len > ((PAGE_SIZE - 1) - s->len)) {
s->full = 1; s->full = 1;
......
...@@ -255,7 +255,8 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) ...@@ -255,7 +255,8 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
/* Maximum number of functions to trace before diagnosing a hang */ /* Maximum number of functions to trace before diagnosing a hang */
#define GRAPH_MAX_FUNC_TEST 100000000 #define GRAPH_MAX_FUNC_TEST 100000000
static void __ftrace_dump(bool disable_tracing); static void
__ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode);
static unsigned int graph_hang_thresh; static unsigned int graph_hang_thresh;
/* Wrap the real function entry probe to avoid possible hanging */ /* Wrap the real function entry probe to avoid possible hanging */
...@@ -266,7 +267,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace) ...@@ -266,7 +267,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace)
ftrace_graph_stop(); ftrace_graph_stop();
printk(KERN_WARNING "BUG: Function graph tracer hang!\n"); printk(KERN_WARNING "BUG: Function graph tracer hang!\n");
if (ftrace_dump_on_oops) if (ftrace_dump_on_oops)
__ftrace_dump(false); __ftrace_dump(false, DUMP_ALL);
return 0; return 0;
} }
......
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