tracing: Updates for the next merge window
Subsystems (7)
Steven Rostedt
maintainercover tracing: Updates for the next merge window
Note, I still have more in my queue that need to go through testing.
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
for-next
Head SHA1: e007f5165a2e366579324062a69e56236a97fad3
Dan Carpenter (1):
tracing: Have trace_stack nr_entries compare not be so subtle
Joe Lawrence (1):
scripts/recordmcount.{c,pl}: support -ffunction-sections .text.* section names
Masami Hiramatsu (11):
tracing/uprobes: Add busy check when cleanup all uprobes
tracing: Lock event_mutex before synth_event_mutex
tracing: Simplify creation and deletion of synthetic events
tracing: Integrate similar probe argument parsers
tracing: Add unified dynamic event framework tracing/kprobes: Use dyn_event framework for kprobe events tracing/uprobes: Use dyn_event framework for uprobe events
tracing: Use dyn_event framework for synthetic events
tracing: Remove unneeded synth_event_mutex
tracing: Add generic event-name based remove event method selftests/ftrace: Add testcases for dynamic event
Steven Rostedt (VMware) (17):
function_graph: Remove unused task_curr_ret_stack()
tracing: Do not line wrap short line in function_graph_enter()
fgraph: Create a fgraph.c file to store function graph infrastructure
fgraph: Have set_graph_notrace only affect function_graph tracer
arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
function_graph: Remove the use of FTRACE_NOTRACE_DEPTH
ftrace: Create new ftrace_internal.h header
function_graph: Do not expose the graph_time option when profiler is not configured
fgraph: Move function graph specific code into fgraph.c
tracing: Rearrange functions in trace_sched_wakeup.c
fgraph: Add new fgraph_ops structure to enable function graph hooks
function_graph: Move ftrace_graph_ret_addr() to fgraph.c
function_graph: Have profiler use new helper ftrace_graph_get_ret_stack()
ring-buffer: Add percentage of ring buffer full to wake up reader
tracing: Add tracefs file buffer_percentage
tracing: Change default buffer_percent to 50
tracing: Consolidate trace_add/remove_event_call back to the nolock functions
----
Documentation/trace/kprobetrace.rst | 3 + Documentation/trace/uprobetracer.rst | 4 + arch/arm64/kernel/stacktrace.c | 3 - include/linux/ftrace.h | 35 +- include/linux/ring_buffer.h | 4 +- kernel/trace/Kconfig | 6 + kernel/trace/Makefile | 2 + kernel/trace/fgraph.c | 615 +++++++++++++++++++++ kernel/trace/ftrace.c | 471 ++-------------- kernel/trace/ftrace_internal.h | 75 +++ kernel/trace/ring_buffer.c | 94 +++- kernel/trace/trace.c | 72 ++- kernel/trace/trace.h | 13 + kernel/trace/trace_dynevent.c | 217 ++++++++ kernel/trace/trace_dynevent.h | 119 ++++ kernel/trace/trace_events.c | 8 +- kernel/trace/trace_events_hist.c | 316 ++++++----- kernel/trace/trace_functions_graph.c | 334 ++--------- kernel/trace/trace_irqsoff.c | 18 +- kernel/trace/trace_kprobe.c | 353 ++++++------ kernel/trace/trace_probe.c | 74 ++- kernel/trace/trace_probe.h | 9 +- kernel/trace/trace_sched_wakeup.c | 270 +++++---- kernel/trace/trace_selftest.c | 8 +- kernel/trace/trace_stack.c | 2 +- kernel/trace/trace_uprobe.c | 301 +++++----- scripts/recordmcount.c | 2 +- scripts/recordmcount.pl | 13 + .../ftrace/test.d/dynevent/add_remove_kprobe.tc | 30 + .../ftrace/test.d/dynevent/add_remove_synth.tc | 27 + .../ftrace/test.d/dynevent/clear_select_events.tc | 50 ++ .../ftrace/test.d/dynevent/generic_clear_event.tc | 49 ++ 32 files changed, 2176 insertions(+), 1421 deletions(-) create mode 100644 kernel/trace/fgraph.c create mode 100644 kernel/trace/ftrace_internal.h create mode 100644 kernel/trace/trace_dynevent.c create mode 100644 kernel/trace/trace_dynevent.h create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_kprobe.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_synth.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/clear_select_events.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/generic_clear_event.tc
Steven Rostedt
maintainerPATCH 1/30 function_graph: Remove unused task_curr_ret_stack()
From: "Steven Rostedt (VMware)" <[email protected]>
The static inline function task_curr_ret_stack() is unused, remove it.
--- include/linux/ftrace.h | 10 ---------- 1 file changed, 10 deletions(-)
include/linux/ftrace.h +0 -10
@@ -809,11 +809,6 @@ extern void ftrace_graph_init_task(struct task_struct *t);extern void ftrace_graph_exit_task (struct task_struct *t);extern void ftrace_graph_init_idle_task (struct task_struct *t,int cpu); -static inline int task_curr_ret_stack (struct task_struct *t) -{ -return t->curr_ret_stack; -} -static inline void pause_graph_tracing (void ) {atomic_inc (¤t->tracing_graph_pause);
@@ -838,11 +833,6 @@ static inline int register_ftrace_graph(trace_func_graph_ret_t retfunc, }static inline void unregister_ftrace_graph (void ) { } -static inline int task_curr_ret_stack (struct task_struct *tsk) -{ -return -1 ; -} -static inline unsigned long ftrace_graph_ret_addr (struct task_struct *task,int *idx,unsigned long ret,unsigned long *retp)
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 2/30 tracing: Do not line wrap short line in function_graph_enter()
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Commit 588ca1786f2dd ("function_graph: Use new curr_ret_depth to manage depth instead of curr_ret_stack") removed a parameter from the call ftrace_push_return_trace() that made it so that the entire call was under 80 characters, but it did not remove the line break. There's no reason to break that line up, so make it a single line.
--- kernel/trace/trace_functions_graph.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/trace/trace_functions_graph.c +1 -2
@@ -188,8 +188,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, trace.func = func; trace.depth = ++current->curr_ret_depth; -if (ftrace_push_return_trace (ret, func, - frame_pointer, retp)) +if (ftrace_push_return_trace (ret, func, frame_pointer, retp)) goto out;/* Only trace if the calling function expects to */
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 3/30 fgraph: Create a fgraph.c file to store function graph infrastructure
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
As the function graph infrastructure can be used by thing other than tracing, moving the code to its own file out of the trace_functions_graph.c code makes more sense.
The fgraph.c file will only contain the infrastructure required to hook into functions and their return code.
--- kernel/trace/Makefile | 1 + kernel/trace/fgraph.c | 232 +++++++++++++++++++++++++++ kernel/trace/trace_functions_graph.c | 220 ------------------------- 3 files changed, 233 insertions(+), 220 deletions(-) create mode 100644 kernel/trace/fgraph.c
kernel/trace/Makefile +1 -0
@@ -57,6 +57,7 @@ obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o +obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += fgraph.o ifeq ($(CONFIG_BLOCK),y) obj-$(CONFIG_EVENT_TRACING) += blktrace.o endif
kernel/trace/fgraph.c new file +232 -0
232 lines not on this page · raw message
kernel/trace/trace_functions_graph.c +0 -220
232 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 4/30 fgraph: Have set_graph_notrace only affect function_graph tracer
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
In order to make the function graph infrastructure more generic, there can not be code specific for the function_graph tracer in the generic code. This includes the set_graph_notrace logic, that stops all graph calls when a function in the set_graph_notrace is hit.
By using the trace_recursion mask, we can use a bit in the current task_struct to implement the notrace code, and move the logic out of fgraph.c and into trace_functions_graph.c and keeps it affecting only the tracer and not all call graph callbacks.
--- kernel/trace/fgraph.c | 21 --------------------- kernel/trace/trace.h | 7 +++++++ kernel/trace/trace_functions_graph.c | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 21 deletions(-)
kernel/trace/fgraph.c +0 -21
@@ -64,30 +64,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,return -EBUSY ; } -/* -* The curr_ret_stack is an index to ftrace return stack of -* current task. Its value should be in [0, FTRACE_RETFUNC_ -* DEPTH) when the function graph tracer is used. To support -* filtering out specific functions, it makes the index -* negative by subtracting huge value (FTRACE_NOTRACE_DEPTH) -* so when it sees a negative index the ftrace will ignore -* the record. And the index gets recovered when returning -* from the filtered function by adding the FTRACE_NOTRACE_ -* DEPTH and then it'll continue to record functions normally. -* -* The curr_ret_stack is initialized to -1 and get increased -* in this function. So it can be less than -1 only if it was -* filtered out via ftrace_graph_notrace_addr() which can be -* set from set_graph_notrace file in tracefs by user. -*/ -if (current->curr_ret_stack <-1 ) -return -EBUSY ; - calltime =trace_clock_local (); index = ++current->curr_ret_stack; -if (ftrace_graph_notrace_addr (func)) - current->curr_ret_stack -=FTRACE_NOTRACE_DEPTH ;barrier (); current->ret_stack[index].ret = ret; current->ret_stack[index].func = func;
kernel/trace/trace.h +7 -0
@@ -534,6 +534,13 @@ enum {TRACE_GRAPH_DEPTH_START_BIT ,TRACE_GRAPH_DEPTH_END_BIT , + +/* +* To implement set_graph_notrace, if this bit is set, we ignore +* function graph tracing of called functions, until the return +* function is called to clear it. +*/ + TRACE_GRAPH_NOTRACE_BIT, };#define trace_recursion_set (bit) do { (current)->trace_recursion |= (1<<(bit)); } while (0)
kernel/trace/trace_functions_graph.c +22 -0
@@ -188,6 +188,18 @@ int trace_graph_entry(struct ftrace_graph_ent *trace)int cpu;int pc; +if (trace_recursion_test (TRACE_GRAPH_NOTRACE_BIT )) +return 0 ; + +if (ftrace_graph_notrace_addr (trace->func)) { +trace_recursion_set (TRACE_GRAPH_NOTRACE_BIT ); +/* +* Need to return 1 to have the return called +* that will clear the NOTRACE bit. +*/ +return 1 ; + } +if (!ftrace_trace_task (tr))return 0 ;
@@ -290,6 +302,11 @@ void trace_graph_return(struct ftrace_graph_ret *trace)ftrace_graph_addr_finish (trace); +if (trace_recursion_test (TRACE_GRAPH_NOTRACE_BIT )) { +trace_recursion_clear (TRACE_GRAPH_NOTRACE_BIT ); +return ; + } +local_irq_save (flags); cpu =raw_smp_processor_id (); data =per_cpu_ptr (tr->trace_buffer.data, cpu);
@@ -315,6 +332,11 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace) {ftrace_graph_addr_finish (trace); +if (trace_recursion_test (TRACE_GRAPH_NOTRACE_BIT )) { +trace_recursion_clear (TRACE_GRAPH_NOTRACE_BIT ); +return ; + } +if (tracing_thresh && (trace->rettime - trace->calltime < tracing_thresh))return ;
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 5/30 arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Functions in the set_graph_notrace no longer subtract FTRACE_NOTRACE_DEPTH from curr_ret_stack, as that is now implemented via the trace_recursion flags. Access to curr_ret_stack no longer needs to worry about checking for this. curr_ret_stack is still initialized to -1, when there's not a shadow stack allocated.
--- arch/arm64/kernel/stacktrace.c | 3 --- 1 file changed, 3 deletions(-)
arch/arm64/kernel/stacktrace.c +0 -3
@@ -61,9 +61,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) (frame->pc == (unsigned long)return_to_handler)) {if (WARN_ON_ONCE (frame->graph ==-1 ))return -EINVAL ; -if (frame->graph <-1 ) - frame->graph +=FTRACE_NOTRACE_DEPTH ; -/* * This is a case where function graph tracer has * modified a return address (LR) in a stack frame
Signature · 8 lines
2.19.1 _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Steven Rostedt
maintainerRe: [for-next][PATCH 05/30] arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
Quoted 10 lines · On Wed, 05 Dec 2018 18:47:54 -0500 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
Functions in the
set_graph_notraceno longer subtractFTRACE_NOTRACE_DEPTHfromcurr_ret_stack, as that is now implemented via thetrace_recursionflags. Access tocurr_ret_stackno longer needs to worry about checking for this.curr_ret_stackis still initialized to -1, when there's not a shadow stack allocated.Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>
I haven't pushed this to Linux next yet. I tested the entire tree as well as crossed compiled it against arm64.
Can you give me an ack for this patch?
Thanks!
-- Steve
Quoted 21 lines
Reviewed-by Joel Fernandes (Google) <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>--- arch/arm64/kernel/stacktrace.c | 3 --- 1 file changed, 3 deletions(-)arch/arm64/kernel/stacktrace.c +0
-3@@ -61,9 +61,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) (frame->pc == (unsigned long)return_to_handler)) {if (WARN_ON_ONCE (frame->graph ==-1 ))return -EINVAL ; -if (frame->graph <-1 ) - frame->graph +=FTRACE_NOTRACE_DEPTH ; -/* * This is a case where function graph tracer has * modified a return address (LR) in a stack frame
Re: [for-next][PATCH 05/30] arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
In reply to Steven RostedtQuoted 22 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Functions in the
set_graph_notraceno longer subtractFTRACE_NOTRACE_DEPTHfromcurr_ret_stack, as that is now implemented via thetrace_recursionflags. Access tocurr_ret_stackno longer needs to worry about checking for this.curr_ret_stackis still initialized to -1, when there's not a shadow stack allocated.Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>Reviewed-by Joel Fernandes (Google) <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>--- arch/arm64/kernel/stacktrace.c | 3 --- 1 file changed, 3 deletions(-)arch/arm64/kernel/stacktrace.c +0
-0
arch/arm64/kernel/stacktrace.c int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) · line 66
Quoted 6 lines
arch/arm64/kernel/stacktrace.c +0
-3@@ -61,9 +61,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) (frame->pc == (unsigned long)return_to_handler)) {if (WARN_ON_ONCE (frame->graph ==-1 ))return -EINVAL ; -if (frame->graph <-1 ) - frame->graph +=FTRACE_NOTRACE_DEPTH ; -
Will
Steven Rostedt
maintainerRe: [for-next][PATCH 05/30] arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
Quoted 18 lines · On Thu, 6 Dec 2018 15:49:32 +0000 Will Deacon <[email protected]> wrote:
Quoted 17 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Functions in the
set_graph_notraceno longer subtractFTRACE_NOTRACE_DEPTHfromcurr_ret_stack, as that is now implemented via thetrace_recursionflags. Access tocurr_ret_stackno longer needs to worry about checking for this.curr_ret_stackis still initialized to -1, when there's not a shadow stack allocated.Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>Reviewed-by Joel Fernandes (Google) <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>--- arch/arm64/kernel/stacktrace.c | 3 --- 1 file changed, 3 deletions(-)
Quoted 14 lines
arch/arm64/kernel/stacktrace.c int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) · line 66
Quoted 11 lines
arch/arm64/kernel/stacktrace.c +0
-3@@ -61,9 +61,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) (frame->pc == (unsigned long)return_to_handler)) {if (WARN_ON_ONCE (frame->graph ==-1 ))return -EINVAL ; -if (frame->graph <-1 ) - frame->graph +=FTRACE_NOTRACE_DEPTH ; -Acked-by Will Deacon <[email protected]>
Thanks Will!
-- Steve
Steven Rostedt
maintainerPATCH 6/30 function_graph: Remove the use of FTRACE_NOTRACE_DEPTH
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
The curr_ret_stack is no longer set to a negative value when a function is not to be traced by the function graph tracer. Remove the usage of FTRACE_NOTRACE_DEPTH, as it is no longer needed.
--- include/linux/ftrace.h | 1 - kernel/trace/fgraph.c | 19 ------------------- kernel/trace/trace_functions_graph.c | 11 ----------- 3 files changed, 31 deletions(-)
include/linux/ftrace.h +0 -1
@@ -790,7 +790,6 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,*/ #define __notrace_funcgraph notrace -#define FTRACE_NOTRACE_DEPTH 65536#define FTRACE_RETFUNC_DEPTH 50#define FTRACE_RETSTACK_ALLOC_SIZE 32 extern int register_ftrace_graph(trace_func_graph_ret_t retfunc,
kernel/trace/fgraph.c +0 -19
@@ -112,16 +112,6 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, index = current->curr_ret_stack; -/* -* A negative index here means that it's just returned from a -* notrace'd function. Recover index to get an original -* return address. See ftrace_push_return_trace(). -* -* TODO: Need to check whether the stack gets corrupted. -*/ -if (index <0 ) - index +=FTRACE_NOTRACE_DEPTH ; -if (unlikely (index <0 || index >=FTRACE_RETFUNC_DEPTH )) {ftrace_graph_stop ();WARN_ON (1 );
@@ -190,15 +180,6 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)*/ barrier (); current->curr_ret_stack--; -/* -* The curr_ret_stack can be less than -1 only if it was -* filtered out and it's about to return from the function. -* Recover the index and continue to trace normal functions. -*/ -if (current->curr_ret_stack <-1 ) { - current->curr_ret_stack +=FTRACE_NOTRACE_DEPTH ; -return ret; - }if (unlikely (!ret)) {ftrace_graph_stop ();
kernel/trace/trace_functions_graph.c +0 -11
@@ -115,9 +115,6 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,if (ret != (unsigned long )return_to_handler)return ret; -if (index <-1 ) - index +=FTRACE_NOTRACE_DEPTH ; -if (index <0 )return ret;
@@ -675,10 +672,6 @@ print_graph_entry_leaf(struct trace_iterator *iter, cpu_data =per_cpu_ptr (data->cpu_data, cpu); -/* If a graph tracer ignored set_graph_notrace */ -if (call->depth <-1 ) - call->depth +=FTRACE_NOTRACE_DEPTH ; -/* * Comments display at + 1 to depth. Since * this is a leaf function, keep the comments
@@ -721,10 +714,6 @@ print_graph_entry_nested(struct trace_iterator *iter,struct fgraph_cpu_data *cpu_data;int cpu = iter->cpu; -/* If a graph tracer ignored set_graph_notrace */ -if (call->depth <-1 ) - call->depth +=FTRACE_NOTRACE_DEPTH ; - cpu_data =per_cpu_ptr (data->cpu_data, cpu); cpu_data->depth = call->depth;
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 7/30 ftrace: Create new ftrace_internal.h header
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
In order to move function graph infrastructure into its own file (fgraph.h) it needs to access various functions and variables in ftrace.c that are currently static. Create a new file called ftrace-internal.h that holds the function prototypes and the extern declarations of the variables needed by fgraph.c as well, and make them global in ftrace.c such that they can be used outside that file.
--- kernel/trace/ftrace.c | 76 +++++++--------------------------- kernel/trace/ftrace_internal.h | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 62 deletions(-) create mode 100644 kernel/trace/ftrace_internal.h
kernel/trace/ftrace.c +14 -62
@@ -40,6 +40,7 @@#include <asm/sections.h> #include <asm/setup.h> +#include "ftrace_internal.h" #include "trace_output.h" #include "trace_stat.h"
@@ -77,7 +78,7 @@#define ASSIGN_OPS_HASH (opsname, val)#endif -static struct ftrace_ops ftrace_list_end__read_mostly = { +struct ftrace_ops ftrace_list_end__read_mostly = { .func = ftrace_stub, .flags =FTRACE_OPS_FL_RECURSION_SAFE |FTRACE_OPS_FL_STUB ,INIT_OPS_HASH (ftrace_list_end)
@@ -112,11 +113,11 @@ static void ftrace_update_trampoline(struct ftrace_ops *ops);*/ static int ftrace_disabled__read_mostly ; -static DEFINE_MUTEX (ftrace_lock); +DEFINE_MUTEX (ftrace_lock); -static struct ftrace_ops __rcu *ftrace_ops_list__read_mostly = &ftrace_list_end; +struct ftrace_ops __rcu *ftrace_ops_list__read_mostly = &ftrace_list_end;ftrace_func_t ftrace_trace_function__read_mostly = ftrace_stub; -static struct ftrace_ops global_ops; +struct ftrace_ops global_ops;#if ARCH_SUPPORTS_FTRACE_OPS static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
@@ -127,26 +128,6 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)#endif -/* -* Traverse the ftrace_global_list, invoking all entries. The reason that we -* can use rcu_dereference_raw_notrace() is that elements removed from this list -* are simply leaked, so there is no need to interact with a grace-period -* mechanism. The rcu_dereference_raw_notrace() calls are needed to handle -* concurrent insertions into the ftrace_global_list. -* -* Silly Alpha and silly pointer-speculation compiler optimizations! -*/ -#define do_for_each_ftrace_op (op, list) \ - op = rcu_dereference_raw_notrace(list); \ - do - -/* -* Optimized for just a single item in the list (as that is the normal case). -*/ -#define while_for_each_ftrace_op (op) \ - while (likely(op = rcu_dereference_raw_notrace((op)->next)) && \ - unlikely((op) != &ftrace_list_end)) -static inline void ftrace_ops_init (struct ftrace_ops *ops) {#ifdef CONFIG_DYNAMIC_FTRACE
@@ -187,17 +168,11 @@ static void ftrace_sync_ipi(void *data) }#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static void update_function_graph_func (void ); -/* Both enabled by default (can be cleared by function_graph tracer flags */ static bool fgraph_sleep_time = true;static bool fgraph_graph_time = true; - -#else -static inline void update_function_graph_func (void ) { }#endif -static ftrace_func_t ftrace_ops_get_list_func (struct ftrace_ops *ops) {/*
@@ -334,7 +309,7 @@ static int remove_ftrace_ops(struct ftrace_ops __rcu **list,static void ftrace_update_trampoline (struct ftrace_ops *ops); -static int __register_ftrace_function (struct ftrace_ops *ops) +int __register_ftrace_function (struct ftrace_ops *ops) {if (ops->flags &FTRACE_OPS_FL_DELETED )return -EINVAL ;
@@ -375,7 +350,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops)return 0 ; } -static int __unregister_ftrace_function (struct ftrace_ops *ops) +int __unregister_ftrace_function (struct ftrace_ops *ops) {int ret;
@@ -1022,9 +997,7 @@ static __init void ftrace_profile_tracefs(struct dentry *d_tracer)#endif /* CONFIG_FUNCTION_PROFILER */ #ifdef CONFIG_FUNCTION_GRAPH_TRACER -static int ftrace_graph_active; -#else -# define ftrace_graph_active 0 +int ftrace_graph_active;#endif #ifdef CONFIG_DYNAMIC_FTRACE
@@ -1067,7 +1040,7 @@ static const struct ftrace_hash empty_hash = { };#define EMPTY_HASH ((struct ftrace_hash *)&empty_hash) -static struct ftrace_ops global_ops = { +struct ftrace_ops global_ops = { .func = ftrace_stub, .local_hash.notrace_hash =EMPTY_HASH , .local_hash.filter_hash =EMPTY_HASH ,
@@ -1503,7 +1476,7 @@ static bool hash_contains_ip(unsigned long ip,* This needs to be called with preemption disabled as * the hashes are freed with call_rcu_sched(). */ -static int +int ftrace_ops_test (struct ftrace_ops *ops,unsigned long ip,void *regs) {struct ftrace_ops_hash hash;
@@ -2682,7 +2655,7 @@ static void ftrace_startup_all(int command) update_all_ops = false; } -static int ftrace_startup (struct ftrace_ops *ops,int command) +int ftrace_startup (struct ftrace_ops *ops,int command) {int ret;
@@ -2724,7 +2697,7 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)return 0 ; } -static int ftrace_shutdown (struct ftrace_ops *ops,int command) +int ftrace_shutdown (struct ftrace_ops *ops,int command) {int ret;
@@ -6177,7 +6150,7 @@ void ftrace_init_trace_array(struct trace_array *tr) } #else -static struct ftrace_ops global_ops = { +struct ftrace_ops global_ops = { .func = ftrace_stub, .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED |
@@ -6194,31 +6167,10 @@ core_initcall(ftrace_nodyn_init);static inline int ftrace_init_dyn_tracefs (struct dentry *d_tracer) {return 0 ; }static inline void ftrace_startup_enable (int command) { }static inline void ftrace_startup_all (int command) { } -/* Keep as macros so we do not need to define the commands */ -# define ftrace_startup (ops, command) \ - ({ \ - int ___ret = __register_ftrace_function(ops); \ - if (!___ret) \ - (ops)->flags |= FTRACE_OPS_FL_ENABLED; \ - ___ret; \ - }) -# define ftrace_shutdown (ops, command) \ - ({ \ - int ___ret = __unregister_ftrace_function(ops); \ - if (!___ret) \ - (ops)->flags &= ~FTRACE_OPS_FL_ENABLED; \ - ___ret; \ - })# define ftrace_startup_sysctl () do { } while (0)# define ftrace_shutdown_sysctl () do { } while (0) -static inline int -ftrace_ops_test (struct ftrace_ops *ops,unsigned long ip,void *regs) -{ -return 1 ; -} -static void ftrace_update_trampoline (struct ftrace_ops *ops) { }
@@ -6930,7 +6882,7 @@ static int ftrace_graph_entry_test(struct ftrace_graph_ent *trace)* function against the global ops, and not just trace any function * that any ftrace_ops registered. */ -static void update_function_graph_func (void ) +void update_function_graph_func (void ) {struct ftrace_ops *op;bool do_test = false;
kernel/trace/ftrace_internal.h new file +75 -0
75 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 8/30 function_graph: Do not expose the graph_time option when profiler is not configured
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
When the function profiler is not configured, the "graph_time" option is meaningless, as the function profiler is the only thing that makes use of it. Do not expose it if the profiler is not configured.
--- kernel/trace/trace.h | 5 +++++ kernel/trace/trace_functions_graph.c | 4 ++++ 2 files changed, 9 insertions(+)
kernel/trace/trace.h +5 -0
@@ -862,7 +862,12 @@ static __always_inline bool ftrace_hash_empty(struct ftrace_hash *hash)#define TRACE_GRAPH_PRINT_FILL_MASK (0x3 << TRACE_GRAPH_PRINT_FILL_SHIFT)extern void ftrace_graph_sleep_time_control (bool enable); + +#ifdef CONFIG_FUNCTION_PROFILER extern void ftrace_graph_graph_time_control (bool enable); +#else +static inline void ftrace_graph_graph_time_control (bool enable) { } +#endif extern enum print_line_t print_graph_function_flags (struct trace_iterator *iter,u32 flags);
kernel/trace/trace_functions_graph.c +4 -0
@@ -60,8 +60,12 @@ static struct tracer_opt trace_opts[] = { { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) },/* Include sleep time (scheduled out) between entry and return */ { TRACER_OPT(sleep-time, TRACE_GRAPH_SLEEP_TIME) }, + +#ifdef CONFIG_FUNCTION_PROFILER /* Include time within nested functions */ { TRACER_OPT(graph-time, TRACE_GRAPH_GRAPH_TIME) }, +#endif + { } /* Empty entry */ };
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 9/30 fgraph: Move function graph specific code into fgraph.c
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
To make the function graph infrastructure more managable, the code needs to be in its own file (fgraph.c). Move the code that is specific for managing the function graph infrastructure out of ftrace.c and into fgraph.c
--- kernel/trace/fgraph.c | 360 ++++++++++++++++++++++++++++++++++++++++- kernel/trace/ftrace.c | 368 +----------------------------------------- 2 files changed, 366 insertions(+), 362 deletions(-)
kernel/trace/fgraph.c +359 -1
@@ -7,11 +7,27 @@* * Highly modified by Steven Rostedt (VMware). */ +#include <linux/suspend.h> #include <linux/ftrace.h> +#include <linux/slab.h> -#include "trace.h" +#include <trace/events/sched.h> + +#include "ftrace_internal.h" + +#ifdef CONFIG_DYNAMIC_FTRACE +#define ASSIGN_OPS_HASH (opsname, val) \ + .func_hash = val, \ + .local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock), +#else +#define ASSIGN_OPS_HASH (opsname, val) +#endif static bool kill_ftrace_graph; +int ftrace_graph_active; + +/* Both enabled by default (can be cleared by function_graph tracer flags */ +static bool fgraph_sleep_time = true;/** * ftrace_graph_is_dead - returns true if ftrace_graph_stop() was called
@@ -161,6 +177,31 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,barrier (); } +/* +* Hibernation protection. +* The state of the current task is too much unstable during +* suspend/restore to disk. We want to protect against that. +*/ +static int +ftrace_suspend_notifier_call (struct notifier_block *bl,unsigned long state, +void *unused) +{ +switch (state) { +case PM_HIBERNATION_PREPARE : +pause_graph_tracing (); +break ; + +case PM_POST_HIBERNATION : +unpause_graph_tracing (); +break ; + } +return NOTIFY_DONE ; +} + +static struct notifier_block ftrace_suspend_notifier = { + .notifier_call = ftrace_suspend_notifier_call, +}; +/* * Send the trace to the ring-buffer. * @return the original return address.
@@ -190,3 +231,320 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)return ret; } + +static struct ftrace_ops graph_ops = { + .func = ftrace_stub, + .flags =FTRACE_OPS_FL_RECURSION_SAFE | +FTRACE_OPS_FL_INITIALIZED | +FTRACE_OPS_FL_PID | +FTRACE_OPS_FL_STUB , +#ifdef FTRACE_GRAPH_TRAMP_ADDR + .trampoline =FTRACE_GRAPH_TRAMP_ADDR , +/* trampoline_size is only needed for dynamically allocated tramps */ +#endif +ASSIGN_OPS_HASH (graph_ops, &global_ops.local_hash) +}; + +void ftrace_graph_sleep_time_control (bool enable) +{ + fgraph_sleep_time = enable; +} + +int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace) +{ +return 0 ; +} + +/* The callbacks that hook a function */ +trace_func_graph_ret_t ftrace_graph_return = + (trace_func_graph_ret_t )ftrace_stub; +trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub; +static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub; + +/* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */ +static int alloc_retstack_tasklist (struct ftrace_ret_stack **ret_stack_list) +{ +int i; +int ret =0 ; +int start =0 , end =FTRACE_RETSTACK_ALLOC_SIZE ; +struct task_struct *g, *t; + +for (i =0 ; i <FTRACE_RETSTACK_ALLOC_SIZE ; i++) { + ret_stack_list[i] = +kmalloc_array (FTRACE_RETFUNC_DEPTH , +sizeof (struct ftrace_ret_stack ), +GFP_KERNEL ); +if (!ret_stack_list[i]) { + start =0 ; + end = i; + ret = -ENOMEM ; + goto free; + } + } + +read_lock (&tasklist_lock); + do_each_thread(g, t) { +if (start == end) { + ret = -EAGAIN ; + goto unlock; + } + +if (t->ret_stack ==NULL ) { +atomic_set (&t->tracing_graph_pause,0 ); +atomic_set (&t->trace_overrun,0 ); + t->curr_ret_stack =-1 ; + t->curr_ret_depth =-1 ; +/* Make sure the tasks see the -1 first: */ +smp_wmb (); + t->ret_stack = ret_stack_list[start++]; + } + }while_each_thread (g, t); + +unlock: +read_unlock (&tasklist_lock); +free: +for (i = start; i < end; i++) +kfree (ret_stack_list[i]); +return ret; +} + +static void +ftrace_graph_probe_sched_switch (void *ignore,bool preempt, +struct task_struct *prev,struct task_struct *next) +{ +unsigned long long timestamp; +int index; + +/* +* Does the user want to count the time a function was asleep. +* If so, do not update the time stamps. +*/ +if (fgraph_sleep_time) +return ; + + timestamp =trace_clock_local (); + + prev->ftrace_timestamp = timestamp; + +/* only process tasks that we timestamped */ +if (!next->ftrace_timestamp) +return ; + +/* +* Update all the counters in next to make up for the +* time next was sleeping. +*/ + timestamp -= next->ftrace_timestamp; + +for (index = next->curr_ret_stack; index >=0 ; index--) + next->ret_stack[index].calltime += timestamp; +} + +static int ftrace_graph_entry_test (struct ftrace_graph_ent *trace) +{ +if (!ftrace_ops_test (&global_ops, trace->func,NULL )) +return 0 ; +return __ftrace_graph_entry (trace); +} + +/* +* The function graph tracer should only trace the functions defined +* by set_ftrace_filter and set_ftrace_notrace. If another function +* tracer ops is registered, the graph tracer requires testing the +* function against the global ops, and not just trace any function +* that any ftrace_ops registered. +*/ +void update_function_graph_func (void ) +{ +struct ftrace_ops *op; +bool do_test = false; + +/* +* The graph and global ops share the same set of functions +* to test. If any other ops is on the list, then +* the graph tracing needs to test if its the function +* it should call. +*/ +do_for_each_ftrace_op (op, ftrace_ops_list) { +if (op != &global_ops && op != &graph_ops && + op != &ftrace_list_end) { + do_test = true; +/* in double loop, break out with goto */ + goto out; + } + }while_for_each_ftrace_op (op); + out: +if (do_test) + ftrace_graph_entry = ftrace_graph_entry_test; +else + ftrace_graph_entry = __ftrace_graph_entry; +} + +static DEFINE_PER_CPU(struct ftrace_ret_stack *, idle_ret_stack); + +static void +graph_init_task (struct task_struct *t,struct ftrace_ret_stack *ret_stack) +{ +atomic_set (&t->tracing_graph_pause,0 ); +atomic_set (&t->trace_overrun,0 ); + t->ftrace_timestamp =0 ; +/* make curr_ret_stack visible before we add the ret_stack */ +smp_wmb (); + t->ret_stack = ret_stack; +} + +/* +* Allocate a return stack for the idle task. May be the first +* time through, or it may be done by CPU hotplug online. +*/ +void ftrace_graph_init_idle_task (struct task_struct *t,int cpu) +{ + t->curr_ret_stack =-1 ; + t->curr_ret_depth =-1 ; +/* +* The idle task has no parent, it either has its own +* stack or no stack at all. +*/ +if (t->ret_stack) +WARN_ON (t->ret_stack !=per_cpu (idle_ret_stack, cpu)); + +if (ftrace_graph_active) { +struct ftrace_ret_stack *ret_stack; + + ret_stack =per_cpu (idle_ret_stack, cpu); +if (!ret_stack) { + ret_stack = +kmalloc_array (FTRACE_RETFUNC_DEPTH , +sizeof (struct ftrace_ret_stack ), +GFP_KERNEL ); +if (!ret_stack) +return ; +per_cpu (idle_ret_stack, cpu) = ret_stack; + } +graph_init_task (t, ret_stack); + } +} + +/* Allocate a return stack for newly created task */ +void ftrace_graph_init_task (struct task_struct *t) +{ +/* Make sure we do not use the parent ret_stack */ + t->ret_stack =NULL ; + t->curr_ret_stack =-1 ; + t->curr_ret_depth =-1 ; + +if (ftrace_graph_active) { +struct ftrace_ret_stack *ret_stack; + + ret_stack =kmalloc_array (FTRACE_RETFUNC_DEPTH , +sizeof (struct ftrace_ret_stack ), +GFP_KERNEL ); +if (!ret_stack) +return ; +graph_init_task (t, ret_stack); + } +} + +void ftrace_graph_exit_task (struct task_struct *t) +{ +struct ftrace_ret_stack *ret_stack = t->ret_stack; + + t->ret_stack =NULL ; +/* NULL must become visible to IRQs before we free it: */ +barrier (); + +kfree (ret_stack); +} + +/* Allocate a return stack for each task */ +static int start_graph_tracing (void ) +{ +struct ftrace_ret_stack **ret_stack_list; +int ret, cpu; + + ret_stack_list =kmalloc_array (FTRACE_RETSTACK_ALLOC_SIZE , +sizeof (struct ftrace_ret_stack *), +GFP_KERNEL ); + +if (!ret_stack_list) +return -ENOMEM ; + +/* The cpu_boot init_task->ret_stack will never be freed */ +for_each_online_cpu (cpu) { +if (!idle_task (cpu)->ret_stack) +ftrace_graph_init_idle_task (idle_task (cpu), cpu); + } + +do { + ret =alloc_retstack_tasklist (ret_stack_list); + }while (ret == -EAGAIN ); + +if (!ret) { + ret =register_trace_sched_switch (ftrace_graph_probe_sched_switch,NULL ); +if (ret) +pr_info ("ftrace_graph: Couldn't activate tracepoint" +" probe to kernel_sched_switch\n" ); + } + +kfree (ret_stack_list); +return ret; +} + +int register_ftrace_graph (trace_func_graph_ret_t retfunc, +trace_func_graph_ent_t entryfunc) +{ +int ret =0 ; + +mutex_lock (&ftrace_lock); + +/* we currently allow only one tracer registered at a time */ +if (ftrace_graph_active) { + ret = -EBUSY ; + goto out; + } + +register_pm_notifier (&ftrace_suspend_notifier); + + ftrace_graph_active++; + ret =start_graph_tracing (); +if (ret) { + ftrace_graph_active--; + goto out; + } + + ftrace_graph_return = retfunc; + +/* +* Update the indirect function to the entryfunc, and the +* function that gets called to the entry_test first. Then +* call the update fgraph entry function to determine if +* the entryfunc should be called directly or not. +*/ + __ftrace_graph_entry = entryfunc; + ftrace_graph_entry = ftrace_graph_entry_test; +update_function_graph_func (); + + ret =ftrace_startup (&graph_ops,FTRACE_START_FUNC_RET ); +out: +mutex_unlock (&ftrace_lock); +return ret; +} + +void unregister_ftrace_graph (void ) +{ +mutex_lock (&ftrace_lock); + +if (unlikely (!ftrace_graph_active)) + goto out; + + ftrace_graph_active--; + 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 (&graph_ops,FTRACE_STOP_FUNC_RET ); +unregister_pm_notifier (&ftrace_suspend_notifier); +unregister_trace_sched_switch (ftrace_graph_probe_sched_switch,NULL ); + + out: +mutex_unlock (&ftrace_lock); +}
kernel/trace/ftrace.c +7 -361
395 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 10/30 tracing: Rearrange functions in trace_sched_wakeup.c
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Rearrange the functions in trace_sched_wakeup.c so that there are fewer
#ifdef CONFIG_FUNCTION_TRACER and #ifdef CONFIG_FUNCTION_GRAPH_TRACER,
instead of having the #ifdefs spread all over.
No functional change is made.
--- kernel/trace/trace_sched_wakeup.c | 272 ++++++++++++++---------------- 1 file changed, 130 insertions(+), 142 deletions(-)
kernel/trace/trace_sched_wakeup.c +130 -142
@@ -35,26 +35,19 @@ static arch_spinlock_t wakeup_lock =static void wakeup_reset (struct trace_array *tr);static void __wakeup_reset (struct trace_array *tr); +static int start_func_tracer (struct trace_array *tr,int graph); +static void stop_func_tracer (struct trace_array *tr,int graph);static int save_flags;#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static int wakeup_display_graph (struct trace_array *tr,int set);# define is_graph (tr) ((tr)->trace_flags & TRACE_ITER_DISPLAY_GRAPH)#else -static inline int wakeup_display_graph (struct trace_array *tr,int set) -{ -return 0 ; -}# define is_graph (tr) false#endif -#ifdef CONFIG_FUNCTION_TRACER -static int wakeup_graph_entry (struct ftrace_graph_ent *trace); -static void wakeup_graph_return (struct ftrace_graph_ret *trace); -static bool function_enabled;/*
@@ -104,122 +97,8 @@ func_prolog_preempt_disable(struct trace_array *tr,return 0 ; } -/* -* wakeup uses its own tracer function to keep the overhead down: -*/ -static void -wakeup_tracer_call (unsigned long ip,unsigned long parent_ip, -struct ftrace_ops *op,struct pt_regs *pt_regs) -{ -struct trace_array *tr = wakeup_trace; -struct trace_array_cpu *data; -unsigned long flags; -int pc; - -if (!func_prolog_preempt_disable (tr, &data, &pc)) -return ; - -local_irq_save (flags); -trace_function (tr, ip, parent_ip, flags, pc); -local_irq_restore (flags); - -atomic_dec (&data->disabled); -preempt_enable_notrace (); -} - -static int register_wakeup_function (struct trace_array *tr,int graph,int set) -{ -int ret; - -/* 'set' is set if TRACE_ITER_FUNCTION is about to be set */ -if (function_enabled || (!set && !(tr->trace_flags &TRACE_ITER_FUNCTION ))) -return 0 ; - -if (graph) - ret =register_ftrace_graph (&wakeup_graph_return, - &wakeup_graph_entry); -else - ret =register_ftrace_function (tr->ops); - -if (!ret) - function_enabled = true; - -return ret; -} - -static void unregister_wakeup_function (struct trace_array *tr,int graph) -{ -if (!function_enabled) -return ; - -if (graph) -unregister_ftrace_graph (); -else -unregister_ftrace_function (tr->ops); - - function_enabled = false; -} - -static int wakeup_function_set (struct trace_array *tr,u32 mask,int set) -{ -if (!(mask &TRACE_ITER_FUNCTION )) -return 0 ; - -if (set) -register_wakeup_function (tr,is_graph (tr),1 ); -else -unregister_wakeup_function (tr,is_graph (tr)); -return 1 ; -} -#else -static int register_wakeup_function (struct trace_array *tr,int graph,int set) -{ -return 0 ; -} -static void unregister_wakeup_function (struct trace_array *tr,int graph) { } -static int wakeup_function_set (struct trace_array *tr,u32 mask,int set) -{ -return 0 ; -} -#endif /* CONFIG_FUNCTION_TRACER */ - -static int wakeup_flag_changed (struct trace_array *tr,u32 mask,int set) -{ -struct tracer *tracer = tr->current_trace; - -if (wakeup_function_set (tr, mask, set)) -return 0 ; -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -if (mask &TRACE_ITER_DISPLAY_GRAPH ) -return wakeup_display_graph (tr, set); -#endif - -return trace_keep_overwrite (tracer, mask, set); -} -static int start_func_tracer (struct trace_array *tr,int graph) -{ -int ret; - - ret =register_wakeup_function (tr, graph,0 ); - -if (!ret &&tracing_is_enabled ()) - tracer_enabled =1 ; -else - tracer_enabled =0 ; - -return ret; -} - -static void stop_func_tracer (struct trace_array *tr,int graph) -{ - tracer_enabled =0 ; - -unregister_wakeup_function (tr, graph); -} - -#ifdef CONFIG_FUNCTION_GRAPH_TRACER static int wakeup_display_graph (struct trace_array *tr,int set) {if (!(is_graph (tr) ^ set))
@@ -318,20 +197,94 @@ static void wakeup_print_header(struct seq_file *s) elsetrace_default_header (s ); } +#else /* CONFIG_FUNCTION_GRAPH_TRACER */ +static int wakeup_graph_entry (struct ftrace_graph_ent *trace) +{ +return -1 ; +} +static void wakeup_graph_return (struct ftrace_graph_ret *trace) { } +#endif /* else CONFIG_FUNCTION_GRAPH_TRACER */ +/* +* wakeup uses its own tracer function to keep the overhead down: +*/ static void -__trace_function (struct trace_array *tr, -unsigned long ip,unsigned long parent_ip, -unsigned long flags,int pc) +wakeup_tracer_call (unsigned long ip,unsigned long parent_ip, +struct ftrace_ops *op,struct pt_regs *pt_regs) { -if (is_graph (tr)) -trace_graph_function (tr, ip, parent_ip, flags, pc); +struct trace_array *tr = wakeup_trace; +struct trace_array_cpu *data; +unsigned long flags; +int pc; + +if (!func_prolog_preempt_disable (tr, &data, &pc)) +return ; + +local_irq_save (flags); +trace_function (tr, ip, parent_ip, flags, pc); +local_irq_restore (flags); + +atomic_dec (&data->disabled); +preempt_enable_notrace (); +} + +static int register_wakeup_function (struct trace_array *tr,int graph,int set) +{ +int ret; + +/* 'set' is set if TRACE_ITER_FUNCTION is about to be set */ +if (function_enabled || (!set && !(tr->trace_flags &TRACE_ITER_FUNCTION ))) +return 0 ; + +if (graph) + ret =register_ftrace_graph (&wakeup_graph_return, + &wakeup_graph_entry);else -trace_function (tr, ip, parent_ip, flags, pc); + ret =register_ftrace_function (tr->ops); + +if (!ret) + function_enabled = true; + +return ret; } -#else -#define __trace_function trace_function +static void unregister_wakeup_function (struct trace_array *tr,int graph) +{ +if (!function_enabled) +return ; + +if (graph) +unregister_ftrace_graph (); +else +unregister_ftrace_function (tr->ops); + + function_enabled = false; +} + +static int wakeup_function_set (struct trace_array *tr,u32 mask,int set) +{ +if (!(mask &TRACE_ITER_FUNCTION )) +return 0 ; + +if (set) +register_wakeup_function (tr,is_graph (tr),1 ); +else +unregister_wakeup_function (tr,is_graph (tr)); +return 1 ; +} +#else /* CONFIG_FUNCTION_TRACER */ +static int register_wakeup_function (struct trace_array *tr,int graph,int set) +{ +return 0 ; +} +static void unregister_wakeup_function (struct trace_array *tr,int graph) { } +static int wakeup_function_set (struct trace_array *tr,u32 mask,int set) +{ +return 0 ; +} +#endif /* else CONFIG_FUNCTION_TRACER */ + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER static enum print_line_t wakeup_print_line (struct trace_iterator *iter) {return TRACE_TYPE_UNHANDLED ;
@@ -340,23 +293,58 @@ static enum print_line_t wakeup_print_line(struct trace_iterator *iter)static void wakeup_trace_open (struct trace_iterator *iter) { }static void wakeup_trace_close (struct trace_iterator *iter) { } -#ifdef CONFIG_FUNCTION_TRACER -static int wakeup_graph_entry (struct ftrace_graph_ent *trace) -{ -return -1 ; -} -static void wakeup_graph_return (struct ftrace_graph_ret *trace) { }static void wakeup_print_header (struct seq_file *s) {trace_default_header (s); } -#else -static void wakeup_print_header (struct seq_file *s) +#endif /* !CONFIG_FUNCTION_GRAPH_TRACER */ + +static void +__trace_function (struct trace_array *tr, +unsigned long ip,unsigned long parent_ip, +unsigned long flags,int pc) +{ +if (is_graph (tr)) +trace_graph_function (tr, ip, parent_ip, flags, pc); +else +trace_function (tr, ip, parent_ip, flags, pc); +} + +static int wakeup_flag_changed (struct trace_array *tr,u32 mask,int set) { -trace_latency_header (s); +struct tracer *tracer = tr->current_trace; + +if (wakeup_function_set (tr, mask, set)) +return 0 ; + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +if (mask &TRACE_ITER_DISPLAY_GRAPH ) +return wakeup_display_graph (tr, set); +#endif + +return trace_keep_overwrite (tracer, mask, set); +} + +static int start_func_tracer (struct trace_array *tr,int graph) +{ +int ret; + + ret =register_wakeup_function (tr, graph,0 ); + +if (!ret &&tracing_is_enabled ()) + tracer_enabled =1 ; +else + tracer_enabled =0 ; + +return ret; +} + +static void stop_func_tracer (struct trace_array *tr,int graph) +{ + tracer_enabled =0 ; + +unregister_wakeup_function (tr, graph); } -#endif /* CONFIG_FUNCTION_TRACER */ -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ /* * Should this new latency be reported/recorded?
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 11/30 fgraph: Add new fgraph_ops structure to enable function graph hooks
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Currently the registering of function graph is to pass in a entry and return function. We need to have a way to associate those functions together where the entry can determine to run the return hook. Having a structure that contains both functions will facilitate the process of converting the code to be able to do such.
This is similar to the way function hooks are enabled (it passes in ftrace_ops). Instead of passing in the functions to use, a single structure is passed in to the registering function.
The unregister function is now passed in the fgraph_ops handle. When we allow more than one callback to the function graph hooks, this will let the system know which one to remove.
--- include/linux/ftrace.h | 21 +++++++++++---------- kernel/trace/fgraph.c | 9 ++++----- kernel/trace/ftrace.c | 10 +++++++--- kernel/trace/trace_functions_graph.c | 21 ++++++++++++++++----- kernel/trace/trace_irqsoff.c | 18 +++++++----------- kernel/trace/trace_sched_wakeup.c | 16 +++++++--------- kernel/trace/trace_selftest.c | 8 ++++++-- 7 files changed, 58 insertions(+), 45 deletions(-)
include/linux/ftrace.h +11 -10
@@ -749,6 +749,11 @@ typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */#ifdef CONFIG_FUNCTION_GRAPH_TRACER +struct fgraph_ops { +trace_func_graph_ent_t entryfunc; +trace_func_graph_ret_t retfunc; +}; +/* * Stack of return addresses for functions * of a thread.
@@ -792,8 +797,9 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,#define FTRACE_RETFUNC_DEPTH 50#define FTRACE_RETSTACK_ALLOC_SIZE 32 -extern int register_ftrace_graph (trace_func_graph_ret_t retfunc, -trace_func_graph_ent_t entryfunc); + +extern int register_ftrace_graph (struct fgraph_ops *ops); +extern void unregister_ftrace_graph (struct fgraph_ops *ops);extern bool ftrace_graph_is_dead (void );extern void ftrace_graph_stop (void );
@@ -802,8 +808,6 @@ extern void ftrace_graph_stop(void);extern trace_func_graph_ret_t ftrace_graph_return;extern trace_func_graph_ent_t ftrace_graph_entry; -extern void unregister_ftrace_graph (void ); -extern void ftrace_graph_init_task (struct task_struct *t);extern void ftrace_graph_exit_task (struct task_struct *t);extern void ftrace_graph_init_idle_task (struct task_struct *t,int cpu);
@@ -825,12 +829,9 @@ 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_init_idle_task (struct task_struct *t,int cpu) { } -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 ) { } +/* Define as macros as fgraph_ops may not be defined */ +#define register_ftrace_graph (ops) ({ -1; }) +#define unregister_ftrace_graph (ops) do { } while (0) static inline unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx, unsigned long ret,
kernel/trace/fgraph.c +4 -5
@@ -490,8 +490,7 @@ static int start_graph_tracing(void)return ret; } -int register_ftrace_graph (trace_func_graph_ret_t retfunc, -trace_func_graph_ent_t entryfunc) +int register_ftrace_graph (struct fgraph_ops *gops) {int ret =0 ;
@@ -512,7 +511,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, goto out; } - ftrace_graph_return = retfunc; + ftrace_graph_return = gops->retfunc;/* * Update the indirect function to the entryfunc, and the
@@ -520,7 +519,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,* call the update fgraph entry function to determine if * the entryfunc should be called directly or not. */ - __ftrace_graph_entry = entryfunc; + __ftrace_graph_entry = gops->entryfunc; ftrace_graph_entry = ftrace_graph_entry_test;update_function_graph_func ();
@@ -530,7 +529,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,return ret; } -void unregister_ftrace_graph (void ) +void unregister_ftrace_graph (struct fgraph_ops *gops) {mutex_lock (&ftrace_lock);
kernel/trace/ftrace.c +7 -3
@@ -849,15 +849,19 @@ static void profile_graph_return(struct ftrace_graph_ret *trace)local_irq_restore (flags); } +static struct fgraph_ops fprofiler_ops = { + .entryfunc = &profile_graph_entry, + .retfunc = &profile_graph_return, +}; +static int register_ftrace_profiler (void ) { -return register_ftrace_graph (&profile_graph_return, - &profile_graph_entry); +return register_ftrace_graph (&fprofiler_ops); }static void unregister_ftrace_profiler (void ) { -unregister_ftrace_graph (); +unregister_ftrace_graph (&fprofiler_ops); }#else static struct ftrace_ops ftrace_profile_ops __read_mostly = {
kernel/trace/trace_functions_graph.c +16 -5
@@ -345,17 +345,25 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace)trace_graph_return (trace); } +static struct fgraph_ops funcgraph_thresh_ops = { + .entryfunc = &trace_graph_entry, + .retfunc = &trace_graph_thresh_return, +}; + +static struct fgraph_ops funcgraph_ops = { + .entryfunc = &trace_graph_entry, + .retfunc = &trace_graph_return, +}; +static int graph_trace_init (struct trace_array *tr) {int ret;set_graph_array (tr);if (tracing_thresh) - ret =register_ftrace_graph (&trace_graph_thresh_return, - &trace_graph_entry); + ret =register_ftrace_graph (&funcgraph_thresh_ops);else - ret =register_ftrace_graph (&trace_graph_return, - &trace_graph_entry); + ret =register_ftrace_graph (&funcgraph_ops);if (ret)return ret;tracing_start_cmdline_record ();
@@ -366,7 +374,10 @@ static int graph_trace_init(struct trace_array *tr)static void graph_trace_reset (struct trace_array *tr) {tracing_stop_cmdline_record (); -unregister_ftrace_graph (); +if (tracing_thresh) +unregister_ftrace_graph (&funcgraph_thresh_ops); +else +unregister_ftrace_graph (&funcgraph_ops); }static int graph_trace_update_thresh (struct trace_array *tr)
kernel/trace/trace_irqsoff.c +7 -11
@@ -218,6 +218,11 @@ static void irqsoff_graph_return(struct ftrace_graph_ret *trace)atomic_dec (&data->disabled); } +static struct fgraph_ops fgraph_ops = { + .entryfunc = &irqsoff_graph_entry, + .retfunc = &irqsoff_graph_return, +}; +static void irqsoff_trace_open (struct trace_iterator *iter) {if (is_graph (iter->tr))
@@ -272,13 +277,6 @@ __trace_function(struct trace_array *tr,#else #define __trace_function trace_function -#ifdef CONFIG_FUNCTION_TRACER -static int irqsoff_graph_entry (struct ftrace_graph_ent *trace) -{ -return -1 ; -} -#endif -static enum print_line_t irqsoff_print_line (struct trace_iterator *iter) {return TRACE_TYPE_UNHANDLED ;
@@ -288,7 +286,6 @@ static void irqsoff_trace_open(struct trace_iterator *iter) { }static void irqsoff_trace_close (struct trace_iterator *iter) { }#ifdef CONFIG_FUNCTION_TRACER -static void irqsoff_graph_return (struct ftrace_graph_ret *trace) { }static void irqsoff_print_header (struct seq_file *s) {trace_default_header (s);
@@ -468,8 +465,7 @@ static int register_irqsoff_function(struct trace_array *tr, int graph, int set)return 0 ;if (graph) - ret =register_ftrace_graph (&irqsoff_graph_return, - &irqsoff_graph_entry); + ret =register_ftrace_graph (&fgraph_ops);else ret =register_ftrace_function (tr->ops);
@@ -485,7 +481,7 @@ static void unregister_irqsoff_function(struct trace_array *tr, int graph)return ;if (graph) -unregister_ftrace_graph (); +unregister_ftrace_graph (&fgraph_ops);else unregister_ftrace_function (tr->ops);
kernel/trace/trace_sched_wakeup.c +7 -9
@@ -162,6 +162,11 @@ static void wakeup_graph_return(struct ftrace_graph_ret *trace)return ; } +static struct fgraph_ops fgraph_wakeup_ops = { + .entryfunc = &wakeup_graph_entry, + .retfunc = &wakeup_graph_return, +}; +static void wakeup_trace_open (struct trace_iterator *iter) {if (is_graph (iter->tr))
@@ -197,12 +202,6 @@ static void wakeup_print_header(struct seq_file *s) else trace_default_header(s); } -#else /* CONFIG_FUNCTION_GRAPH_TRACER */ -static int wakeup_graph_entry (struct ftrace_graph_ent *trace) -{ -return -1 ; -} -static void wakeup_graph_return (struct ftrace_graph_ret *trace) { }#endif /* else CONFIG_FUNCTION_GRAPH_TRACER */ /*
@@ -237,8 +236,7 @@ static int register_wakeup_function(struct trace_array *tr, int graph, int set)return 0 ;if (graph) - ret =register_ftrace_graph (&wakeup_graph_return, - &wakeup_graph_entry); + ret =register_ftrace_graph (&fgraph_wakeup_ops);else ret =register_ftrace_function (tr->ops);
@@ -254,7 +252,7 @@ static void unregister_wakeup_function(struct trace_array *tr, int graph)return ;if (graph) -unregister_ftrace_graph (); +unregister_ftrace_graph (&fgraph_wakeup_ops);else unregister_ftrace_function (tr->ops);
kernel/trace/trace_selftest.c +6 -2
@@ -741,6 +741,11 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace)return trace_graph_entry (trace); } +static struct fgraph_ops fgraph_ops__initdata = { + .entryfunc = &trace_graph_entry_watchdog, + .retfunc = &trace_graph_return, +}; +/* * Pretty much the same than for the function tracer from which the selftest * has been borrowed.
@@ -765,8 +770,7 @@ trace_selftest_startup_function_graph(struct tracer *trace,*/ tracing_reset_online_cpus (&tr->trace_buffer);set_graph_array (tr); - ret =register_ftrace_graph (&trace_graph_return, - &trace_graph_entry_watchdog); + ret =register_ftrace_graph (&fgraph_ops);if (ret) {warn_failed_init_tracer (trace, ret); goto out;
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 12/30 function_graph: Move ftrace_graph_ret_addr() to fgraph.c
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Move the function function_graph_ret_addr() to fgraph.c, as the management of the curr_ret_stack is going to change, and all the accesses to ret_stack needs to be done in fgraph.c.
--- kernel/trace/fgraph.c | 55 ++++++++++++++++++++++++++++ kernel/trace/trace_functions_graph.c | 55 ---------------------------- 2 files changed, 55 insertions(+), 55 deletions(-)
kernel/trace/fgraph.c +55 -0
@@ -232,6 +232,61 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)return ret; } +/** +* ftrace_graph_ret_addr - convert a potentially modified stack return address +* to its original value +* +* This function can be called by stack unwinding code to convert a found stack +* return address ('ret') to its original value, in case the function graph +* tracer has modified it to be 'return_to_handler'. If the address hasn't +* been modified, the unchanged value of 'ret' is returned. +* +* 'idx' is a state variable which should be initialized by the caller to zero +* before the first call. +* +* 'retp' is a pointer to the return address on the stack. It's ignored if +* the arch doesn't have HAVE_FUNCTION_GRAPH_RET_ADDR_PTR defined. +*/ +#ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR +unsigned long ftrace_graph_ret_addr (struct task_struct *task,int *idx, +unsigned long ret,unsigned long *retp) +{ +int index = task->curr_ret_stack; +int i; + +if (ret != (unsigned long )return_to_handler) +return ret; + +if (index <0 ) +return ret; + +for (i =0 ; i <= index; i++) +if (task->ret_stack[i].retp == retp) +return task->ret_stack[i].ret; + +return ret; +} +#else /* !HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */ +unsigned long ftrace_graph_ret_addr (struct task_struct *task,int *idx, +unsigned long ret,unsigned long *retp) +{ +int task_idx; + +if (ret != (unsigned long )return_to_handler) +return ret; + + task_idx = task->curr_ret_stack; + +if (!task->ret_stack || task_idx < *idx) +return ret; + + task_idx -= *idx; + (*idx)++; + +return task->ret_stack[task_idx].ret; +} +#endif /* HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */ +static struct ftrace_ops graph_ops = { .func = ftrace_stub, .flags = FTRACE_OPS_FL_RECURSION_SAFE |
kernel/trace/trace_functions_graph.c +0 -55
61 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 13/30 function_graph: Have profiler use new helper ftrace_graph_get_ret_stack()
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
The ret_stack processing is going to change, and that is going to break anything that is accessing the ret_stack directly. One user is the function graph profiler. By using the ftrace_graph_get_ret_stack() helper function, the profiler can access the ret_stack entry without relying on the implementation details of the stack itself.
--- include/linux/ftrace.h | 3 +++ kernel/trace/fgraph.c | 11 +++++++++++ kernel/trace/ftrace.c | 21 +++++++++++---------- 3 files changed, 25 insertions(+), 10 deletions(-)
include/linux/ftrace.h +3 -0
@@ -785,6 +785,9 @@ extern int function_graph_enter(unsigned long ret, unsigned long func, unsigned long frame_pointer, unsigned long *retp); +struct ftrace_ret_stack * +ftrace_graph_get_ret_stack (struct task_struct *task,int idx); +unsigned long ftrace_graph_ret_addr (struct task_struct *task,int *idx,unsigned long ret,unsigned long *retp);
kernel/trace/fgraph.c +11 -0
@@ -232,6 +232,17 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)return ret; } +struct ftrace_ret_stack * +ftrace_graph_get_ret_stack (struct task_struct *task,int idx) +{ + idx = current->curr_ret_stack - idx; + +if (idx >=0 && idx <= task->curr_ret_stack) +return ¤t->ret_stack[idx]; + +return NULL ; +} +/** * ftrace_graph_ret_addr - convert a potentially modified stack return address * to its original value
kernel/trace/ftrace.c +11 -10
@@ -792,7 +792,7 @@ void ftrace_graph_graph_time_control(bool enable)static int profile_graph_entry (struct ftrace_graph_ent *trace) { -int index = current->curr_ret_stack; +struct ftrace_ret_stack *ret_stack;function_profile_call (trace->func,0 ,NULL ,NULL );
@@ -800,14 +800,16 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace)if (!current->ret_stack)return 0 ; -if (index >=0 && index <FTRACE_RETFUNC_DEPTH ) - current->ret_stack[index].subtime =0 ; + ret_stack =ftrace_graph_get_ret_stack (current,0 ); +if (ret_stack) + ret_stack->subtime =0 ;return 1 ; }static void profile_graph_return (struct ftrace_graph_ret *trace) { +struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;unsigned long long calltime;struct ftrace_profile *rec;
@@ -825,16 +827,15 @@ static void profile_graph_return(struct ftrace_graph_ret *trace) calltime = trace->rettime - trace->calltime;if (!fgraph_graph_time) { -int index; - - index = current->curr_ret_stack;/* Append this call time to the parent time to subtract */ -if (index) - current->ret_stack[index -1 ].subtime += calltime; + ret_stack =ftrace_graph_get_ret_stack (current,1 ); +if (ret_stack) + ret_stack->subtime += calltime; -if (current->ret_stack[index].subtime < calltime) - calltime -= current->ret_stack[index].subtime; + ret_stack =ftrace_graph_get_ret_stack (current,0 ); +if (ret_stack && ret_stack->subtime < calltime) + calltime -= ret_stack->subtime;else calltime =0 ; }
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 14/30 tracing: Have trace_stack nr_entries compare not be so subtle
In reply to Steven RostedtFrom: Dan Carpenter <[email protected]>
Dan Carpenter reviewed the trace_stack.c code and figured he found an off by one bug.
"From reviewing the code, it seems possible for
stack_trace_max.nr_entries to be set to .max_entries and in that case we would be reading one element beyond the end of the stack_dump_trace[] array. If it's not set to .max_entries then the bug doesn't affect runtime."
Although it looks to be the case, it is not. Because we have:
static unsigned long stack_dump_trace[STACK_TRACE_ENTRIES+1] =
{ [0 ... (STACK_TRACE_ENTRIES)] = ULONG_MAX }; struct stack_trace stack_trace_max = {
.max_entries = STACK_TRACE_ENTRIES - 1,
.entries = &stack_dump_trace[0],};
And:
stack_trace_max.nr_entries = x; for (; x < i; x++) stack_dump_trace[x] = ULONG_MAX;
Even if nr_entries equals max_entries, indexing with it into the stack_dump_trace[] array will not overflow the array. But if it is the case, the second part of the conditional that tests stack_dump_trace[nr_entries] to ULONG_MAX will always be true.
By applying Dan's patch, it removes the subtle aspect of it and makes the if conditional slightly more efficient.
--- kernel/trace/trace_stack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_stack.c +1 -1
@@ -286,7 +286,7 @@ __next(struct seq_file *m, loff_t *pos) {long n = *pos -1 ; -if (n > stack_trace_max.nr_entries || stack_dump_trace[n] ==ULONG_MAX ) +if (n >= stack_trace_max.nr_entries || stack_dump_trace[n] ==ULONG_MAX )return NULL ; m->private = (void *)n;
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 15/30 scripts/recordmcount.{c,pl}: support -ffunction-sections .text.* section names
In reply to Steven RostedtFrom: Joe Lawrence <[email protected]>
When building with -ffunction-sections, the compiler will place each function into its own ELF section, prefixed with ".text". For example, a simple test module with functions test_module_do_work() and test_module_wq_func():
% objdump --section-headers test_module.o | awk '/\.text/{print $2}'
.text
.text.test_module_do_work
.text.test_module_wq_func
.init.text
.exit.textAdjust the recordmcount scripts to look for ".text" as a section name prefix. This will ensure that those functions will be included in the __mcount_loc relocations:
% objdump --reloc --section __mcount_loc test_module.o OFFSET TYPE VALUE 0000000000000000 R_X86_64_64 .text.test_module_do_work 0000000000000008 R_X86_64_64 .text.test_module_wq_func 0000000000000010 R_X86_64_64 .init.text
--- scripts/recordmcount.c | 2 +- scripts/recordmcount.pl | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
scripts/recordmcount.c +1 -1
@@ -397,7 +397,7 @@ static uint32_t (*w2)(uint16_t);static int is_mcounted_section_name (char const *const txtname) { -return strcmp (".text" , txtname) ==0 || +return strncmp (".text" , txtname,5 ) ==0 ||strcmp (".init.text" , txtname) ==0 ||strcmp (".ref.text" , txtname) ==0 || strcmp(".sched.text", txtname) == 0 ||
scripts/recordmcount.pl +13 -0
@@ -142,6 +142,11 @@ my %text_sections = ( ".text.unlikely" => 1, ); +# Acceptable section-prefixes to record. +my %text_section_prefixes = ( + ".text." => 1, +); + # Note: we are nice to C-programmers here, thus we skip the '||='-idiom. $objdump = 'objdump' if (!$objdump); $objcopy = 'objcopy' if (!$objcopy);
@@ -519,6 +524,14 @@ while (<IN>) { # Only record text sections that we know are safe $read_function = defined($text_sections{$1}); + if (!$read_function) { + foreach my $prefix (keys %text_section_prefixes) { + if (substr($1, 0, length $prefix) eq $prefix) { + $read_function = 1; + last; + } + } + } # print out any recorded offsets update_funcs();
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 16/30 ring-buffer: Add percentage of ring buffer full to wake up reader
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Instead of just waiting for a page to be full before waking up a pending reader, allow the reader to pass in a "percentage" of pages that have content before waking up a reader. This should help keep the process of reading the events not cause wake ups that constantly cause reading of the buffer.
--- include/linux/ring_buffer.h | 4 ++- kernel/trace/ring_buffer.c | 71 ++++++++++++++++++++++++++++++++++--- kernel/trace/trace.c | 8 ++--- 3 files changed, 73 insertions(+), 10 deletions(-)
include/linux/ring_buffer.h +3 -1
@@ -97,7 +97,7 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k__ring_buffer_alloc ((size), (flags), &__key); \ }) -int ring_buffer_wait (struct ring_buffer *buffer,int cpu,bool full); +int ring_buffer_wait (struct ring_buffer *buffer,int cpu,int full);__poll_t ring_buffer_poll_wait (struct ring_buffer *buffer,int cpu,struct file *filp,poll_table *poll_table);
@@ -189,6 +189,8 @@ bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer);size_t ring_buffer_page_len (void *page); +size_t ring_buffer_nr_pages (struct ring_buffer *buffer,int cpu); +size_t ring_buffer_nr_dirty_pages (struct ring_buffer *buffer,int cpu);void *ring_buffer_alloc_read_page (struct ring_buffer *buffer,int cpu);void ring_buffer_free_read_page (struct ring_buffer *buffer,int cpu,void *data);
kernel/trace/ring_buffer.c +66 -5
138 lines not on this page · raw message
kernel/trace/trace.c +4 -4
@@ -1431,7 +1431,7 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) }#endif /* CONFIG_TRACER_MAX_TRACE */ -static int wait_on_pipe (struct trace_iterator *iter,bool full) +static int wait_on_pipe (struct trace_iterator *iter,int full) {/* Iterators are static, they should be filled or empty */ if (trace_buffer_iter (iter, iter->cpu_file))
@@ -5693,7 +5693,7 @@ static int tracing_wait_pipe(struct file *filp)mutex_unlock (&iter->mutex); - ret =wait_on_pipe (iter, false); + ret =wait_on_pipe (iter,0 );mutex_lock (&iter->mutex);
@@ -6751,7 +6751,7 @@ tracing_buffers_read(struct file *filp, char __user *ubuf,if ((filp->f_flags &O_NONBLOCK ))return -EAGAIN ; - ret =wait_on_pipe (iter, false); + ret =wait_on_pipe (iter,0 );if (ret)return ret;
@@ -6948,7 +6948,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,if ((file->f_flags &O_NONBLOCK ) || (flags &SPLICE_F_NONBLOCK )) goto out; - ret =wait_on_pipe (iter, true); + ret =wait_on_pipe (iter,1 );if (ret) goto out;
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 17/30 tracing: Add tracefs file buffer_percentage
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Add a "buffer_percentage" file, that allows users to specify how much of the buffer (percentage of pages) need to be filled before waking up a task blocked on a per cpu trace_pipe_raw file.
--- kernel/trace/ring_buffer.c | 39 ++++++++++++++++----------- kernel/trace/trace.c | 54 +++++++++++++++++++++++++++++++++++++- kernel/trace/trace.h | 1 + 3 files changed, 77 insertions(+), 17 deletions(-)
kernel/trace/ring_buffer.c +23 -16
@@ -489,6 +489,7 @@ struct ring_buffer_per_cpu {local_t commits;local_t pages_touched;local_t pages_read; +long last_pages_touch;size_t shortest_full;unsigned long read;unsigned long read_bytes;
@@ -2632,7 +2633,9 @@ static void rb_commit(struct ring_buffer_per_cpu *cpu_buffer, static __always_inline voidrb_wakeups (struct ring_buffer *buffer,struct ring_buffer_per_cpu *cpu_buffer) { -bool pagebusy; +size_t nr_pages; +size_t dirty; +size_t full;if (buffer->irq_work.waiters_pending) { buffer->irq_work.waiters_pending = false;
@@ -2646,24 +2649,27 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer)irq_work_queue (&cpu_buffer->irq_work.work); } - pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page; +if (cpu_buffer->last_pages_touch ==local_read (&cpu_buffer->pages_touched)) +return ; -if (!pagebusy && cpu_buffer->irq_work.full_waiters_pending) { -size_t nr_pages; -size_t dirty; -size_t full; +if (cpu_buffer->reader_page == cpu_buffer->commit_page) +return ; - full = cpu_buffer->shortest_full; - nr_pages = cpu_buffer->nr_pages; - dirty =ring_buffer_nr_dirty_pages (buffer, cpu_buffer->cpu); -if (full && nr_pages && (dirty *100 ) <= full * nr_pages) -return ; +if (!cpu_buffer->irq_work.full_waiters_pending) +return ; - cpu_buffer->irq_work.wakeup_full = true; - cpu_buffer->irq_work.full_waiters_pending = false; -/* irq_work_queue() supplies it's own memory barriers */ -irq_work_queue (&cpu_buffer->irq_work.work); - } + cpu_buffer->last_pages_touch =local_read (&cpu_buffer->pages_touched); + + full = cpu_buffer->shortest_full; + nr_pages = cpu_buffer->nr_pages; + dirty =ring_buffer_nr_dirty_pages (buffer, cpu_buffer->cpu); +if (full && nr_pages && (dirty *100 ) <= full * nr_pages) +return ; + + cpu_buffer->irq_work.wakeup_full = true; + cpu_buffer->irq_work.full_waiters_pending = false; +/* irq_work_queue() supplies it's own memory barriers */ +irq_work_queue (&cpu_buffer->irq_work.work); }/*
@@ -4394,6 +4400,7 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)local_set (&cpu_buffer->commits,0 );local_set (&cpu_buffer->pages_touched,0 );local_set (&cpu_buffer->pages_read,0 ); + cpu_buffer->last_pages_touch =0 ; cpu_buffer->shortest_full =0 ; cpu_buffer->read =0 ; cpu_buffer->read_bytes =0 ;
kernel/trace/trace.c +53 -1
72 lines not on this page · raw message
kernel/trace/trace.h +1 -0
@@ -247,6 +247,7 @@ struct trace_array {int clock_id;int nr_topts;bool clear_trace; +int buffer_percent;struct tracer *current_trace;unsigned int trace_flags;unsigned char trace_flags_index[TRACE_FLAGS_MAX_SIZE ];
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 18/30 tracing: Change default buffer_percent to 50
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
After running several tests, it appears that having the reader wait till half the buffer is full before starting to read (and causing its own events to fill up the ring buffer constantly), works well. It keeps trace-cmd (the main user of this interface) from dominating the traces it records.
--- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace.c +1 -1
@@ -8017,7 +8017,7 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)trace_create_file ("timestamp_mode" ,0444 , d_tracer, tr, &trace_time_stamp_mode_fops); - tr->buffer_percent =1 ; + tr->buffer_percent =50 ;trace_create_file ("buffer_percent" ,0444 , d_tracer, tr, &buffer_percent_fops);
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 19/30 tracing/uprobes: Add busy check when cleanup all uprobes
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Add a busy check loop in cleanup_all_probes() before trying to remove all events in uprobe_events, the same way that kprobe_events does.
Without this change, writing null to uprobe_events will try to remove events but if one of them is enabled, it will stop there leaving some events cleared and others not clceared.
With this change, writing null to uprobe_events makes sure all events are not enabled before removing events. So, it clears all events, or returns an error (-EBUSY) with keeping all events.
--- kernel/trace/trace_uprobe.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/trace/trace_uprobe.c +7 -0
@@ -587,12 +587,19 @@ static int cleanup_all_probes(void)int ret =0 ;mutex_lock (&uprobe_lock); +/* Ensure no probe is in use. */ +list_for_each_entry (tu, &uprobe_list, list) +if (trace_probe_is_enabled (&tu->tp)) { + ret = -EBUSY ; + goto end; + }while (!list_empty (&uprobe_list)) { tu = list_entry(uprobe_list.next, struct trace_uprobe, list); ret =unregister_trace_uprobe (tu);if (ret)break ; } +end:mutex_unlock (&uprobe_lock);return ret; }
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 20/30 tracing: Lock event_mutex before synth_event_mutex
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
synthetic event is using synth_event_mutex for protecting synth_event_list, and event_trigger_write() path acquires locks as below order.
event_trigger_write(event_mutex)
->trigger_process_regex(trigger_cmd_mutex)
->event_hist_trigger_func(synth_event_mutex)
On the other hand, synthetic event creation and deletion paths call trace_add_event_call() and trace_remove_event_call() which acquires event_mutex. In that case, if we keep the synth_event_mutex locked while registering/unregistering synthetic events, its dependency will be inversed.
To avoid this issue, current synthetic event is using a 2 phase process to create/delete events. For example, it searches existing events under synth_event_mutex to check for event-name conflicts, and unlocks synth_event_mutex, then registers a new event under event_mutex locked. Finally, it locks synth_event_mutex and tries to add the new event to the list. But it can introduce complexity and a chance for name conflicts.
To solve this simpler, this introduces trace_add_event_call_nolock() and trace_remove_event_call_nolock() which don't acquire event_mutex inside. synthetic event can lock event_mutex before synth_event_mutex to solve the lock dependency issue simpler.
--- include/linux/trace_events.h | 2 ++ kernel/trace/trace_events.c | 34 ++++++++++++++++++++++++++------ kernel/trace/trace_events_hist.c | 24 ++++++++++------------ 3 files changed, 40 insertions(+), 20 deletions(-)
include/linux/trace_events.h +2 -0
@@ -529,6 +529,8 @@ extern int trace_event_raw_init(struct trace_event_call *call);extern int trace_define_field (struct trace_event_call *call,const char *type,const char *name,int offset,int size,int is_signed,int filter_type); +extern int trace_add_event_call_nolock (struct trace_event_call *call); +extern int trace_remove_event_call_nolock (struct trace_event_call *call);extern int trace_add_event_call (struct trace_event_call *call);extern int trace_remove_event_call (struct trace_event_call *call);extern int trace_event_get_offsets (struct trace_event_call *call);
kernel/trace/trace_events.c +28 -6
@@ -2305,11 +2305,11 @@ __trace_early_add_new_event(struct trace_event_call *call,struct ftrace_module_file_ops ;static void __add_event_to_tracers (struct trace_event_call *call); -/* Add an additional event_call dynamically */ -int trace_add_event_call (struct trace_event_call *call) +int trace_add_event_call_nolock (struct trace_event_call *call) {int ret; -mutex_lock (&event_mutex); +lockdep_assert_held (&event_mutex); +mutex_lock (&trace_types_lock); ret =__register_event (call,NULL );
@@ -2317,6 +2317,16 @@ int trace_add_event_call(struct trace_event_call *call)__add_event_to_tracers (call);mutex_unlock (&trace_types_lock); +return ret; +} + +/* Add an additional event_call dynamically */ +int trace_add_event_call (struct trace_event_call *call) +{ +int ret; + +mutex_lock (&event_mutex); + ret =trace_add_event_call_nolock (call);mutex_unlock (&event_mutex);return ret; }
@@ -2366,17 +2376,29 @@ static int probe_remove_event_call(struct trace_event_call *call)return 0 ; } -/* Remove an event_call */ -int trace_remove_event_call (struct trace_event_call *call) +/* no event_mutex version */ +int trace_remove_event_call_nolock (struct trace_event_call *call) {int ret; -mutex_lock (&event_mutex); +lockdep_assert_held (&event_mutex); +mutex_lock (&trace_types_lock);down_write (&trace_event_sem); ret =probe_remove_event_call (call);up_write (&trace_event_sem);mutex_unlock (&trace_types_lock); + +return ret; +} + +/* Remove an event_call */ +int trace_remove_event_call (struct trace_event_call *call) +{ +int ret; + +mutex_lock (&event_mutex); + ret =trace_remove_event_call_nolock (call);mutex_unlock (&event_mutex);return ret;
kernel/trace/trace_events_hist.c +10 -14
@@ -912,7 +912,7 @@ static int register_synth_event(struct synth_event *event) call->data = event; call->tp = event->tp; - ret =trace_add_event_call (call); + ret =trace_add_event_call_nolock (call);if (ret) {pr_warn ("Failed to register synthetic event: %s\n" ,trace_event_name (call));
@@ -936,7 +936,7 @@ static int unregister_synth_event(struct synth_event *event)struct trace_event_call *call = &event->call;int ret; - ret =trace_remove_event_call (call); + ret =trace_remove_event_call_nolock (call);return ret; }
@@ -1013,12 +1013,10 @@ static void add_or_delete_synth_event(struct synth_event *event, int delete)if (delete)free_synth_event (event);else { -mutex_lock (&synth_event_mutex);if (!find_synth_event (event->name))list_add (&event->list, &synth_event_list);else free_synth_event (event); -mutex_unlock (&synth_event_mutex); } }
@@ -1030,6 +1028,7 @@ static int create_synth_event(int argc, char **argv)int i, consumed =0 , n_fields =0 , ret =0 ;char *name; +mutex_lock (&event_mutex);mutex_lock (&synth_event_mutex);/*
@@ -1102,8 +1101,6 @@ static int create_synth_event(int argc, char **argv) goto err; } out: -mutex_unlock (&synth_event_mutex); -if (event) {if (delete_event) { ret =unregister_synth_event (event);
@@ -1113,10 +1110,13 @@ static int create_synth_event(int argc, char **argv)add_or_delete_synth_event (event, ret); } } +mutex_unlock (&synth_event_mutex); +mutex_unlock (&event_mutex);return ret; err:mutex_unlock (&synth_event_mutex); +mutex_unlock (&event_mutex);for (i =0 ; i < n_fields; i++)free_synth_field (fields[i]);
@@ -1127,12 +1127,10 @@ static int create_synth_event(int argc, char **argv)static int release_all_synth_events (void ) { -struct list_head release_events;struct synth_event *event, *e;int ret =0 ; -INIT_LIST_HEAD (&release_events); - +mutex_lock (&event_mutex);mutex_lock (&synth_event_mutex);list_for_each_entry (event, &synth_event_list, list) {
@@ -1142,16 +1140,14 @@ static int release_all_synth_events(void) } } -list_splice_init (&event->list, &release_events); - -mutex_unlock (&synth_event_mutex); - -list_for_each_entry_safe (event, e, &release_events, list) { +list_for_each_entry_safe (event, e, &synth_event_list, list) {list_del (&event->list); ret =unregister_synth_event (event);add_or_delete_synth_event (event, !ret); } +mutex_unlock (&synth_event_mutex); +mutex_unlock (&event_mutex);return ret; }
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 21/30 tracing: Simplify creation and deletion of synthetic events
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Since the event_mutex and synth_event_mutex ordering issue is gone, we can skip existing event check when adding or deleting events, and some redundant code in error path.
This changes release_all_synth_events() to abort the process when it hits any error and returns the error code. It succeeds only if it has no error.
--- kernel/trace/trace_events_hist.c | 53 +++++++++++--------------------- 1 file changed, 18 insertions(+), 35 deletions(-)
kernel/trace/trace_events_hist.c +18 -35
@@ -1008,18 +1008,6 @@ struct hist_var_data {struct hist_trigger_data *hist_data; }; -static void add_or_delete_synth_event (struct synth_event *event,int delete) -{ -if (delete) -free_synth_event (event); -else { -if (!find_synth_event (event->name)) -list_add (&event->list, &synth_event_list); -else -free_synth_event (event); - } -} -static int create_synth_event (int argc,char **argv) {struct synth_field *field, *fields[SYNTH_FIELDS_MAX ];
@@ -1052,15 +1040,16 @@ static int create_synth_event(int argc, char **argv)if (event) {if (delete_event) {if (event->ref) { - event =NULL ; ret = -EBUSY ; goto out; } -list_del (&event->list); - goto out; - } - event =NULL ; - ret = -EEXIST ; + ret =unregister_synth_event (event); +if (!ret) { +list_del (&event->list); +free_synth_event (event); + } + }else + ret = -EEXIST ; goto out; }else if (delete_event) { ret = -ENOENT ;
@@ -1100,29 +1089,21 @@ static int create_synth_event(int argc, char **argv) event =NULL ; goto err; } + ret =register_synth_event (event); +if (!ret) +list_add (&event->list, &synth_event_list); +else +free_synth_event (event); out: -if (event) { -if (delete_event) { - ret =unregister_synth_event (event); -add_or_delete_synth_event (event, !ret); - }else { - ret =register_synth_event (event); -add_or_delete_synth_event (event, ret); - } - }mutex_unlock (&synth_event_mutex);mutex_unlock (&event_mutex);return ret; err: -mutex_unlock (&synth_event_mutex); -mutex_unlock (&event_mutex); -for (i =0 ; i < n_fields; i++)free_synth_field (fields[i]); -free_synth_event (event); -return ret; + goto out; }static int release_all_synth_events (void )
@@ -1141,10 +1122,12 @@ static int release_all_synth_events(void) }list_for_each_entry_safe (event, e, &synth_event_list, list) { -list_del (&event->list); - ret =unregister_synth_event (event); -add_or_delete_synth_event (event, !ret); +if (!ret) { +list_del (&event->list); +free_synth_event (event); + }else +break ; }mutex_unlock (&synth_event_mutex);mutex_unlock (&event_mutex);
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 22/30 tracing: Integrate similar probe argument parsers
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Integrate similar argument parsers for kprobes and uprobes events into traceprobe_parse_probe_arg().
--- kernel/trace/trace_kprobe.c | 48 ++----------------------------------- kernel/trace/trace_probe.c | 47 +++++++++++++++++++++++++++++++++--- kernel/trace/trace_probe.h | 7 ++---- kernel/trace/trace_uprobe.c | 44 ++-------------------------------- 4 files changed, 50 insertions(+), 96 deletions(-)
kernel/trace/trace_kprobe.c +2 -46
@@ -548,7 +548,6 @@ static int create_trace_kprobe(int argc, char **argv)bool is_return = false, is_delete = false;char *symbol =NULL , *event =NULL , *group =NULL ;int maxactive =0 ; -char *arg;long offset =0 ;void *addr =NULL ;char buf[MAX_EVENT_NAME_LEN ];
@@ -676,53 +675,10 @@ static int create_trace_kprobe(int argc, char **argv) }/* parse arguments */ - ret =0 ;for (i =0 ; i < argc && i <MAX_TRACE_ARGS ; i++) { -struct probe_arg *parg = &tk->tp.args[i]; - -/* Increment count for freeing args in error case */ - tk->tp.nr_args++; - -/* Parse argument name */ - arg =strchr (argv[i],'=' ); -if (arg) { - *arg++ ='\0' ; - parg->name =kstrdup (argv[i],GFP_KERNEL ); - }else { - arg = argv[i]; -/* If argument name is omitted, set "argN" */ -snprintf (buf,MAX_EVENT_NAME_LEN ,"arg%d" , i +1 ); - parg->name =kstrdup (buf,GFP_KERNEL ); - } - -if (!parg->name) { -pr_info ("Failed to allocate argument[%d] name.\n" , i); - ret = -ENOMEM ; - goto error; - } - -if (!is_good_name (parg->name)) { -pr_info ("Invalid argument[%d] name: %s\n" , - i, parg->name); - ret = -EINVAL ; - goto error; - } - -if (traceprobe_conflict_field_name (parg->name, - tk->tp.args, i)) { -pr_info ("Argument[%d] name '%s' conflicts with " -"another field.\n" , i, argv[i]); - ret = -EINVAL ; - goto error; - } - -/* Parse fetch argument */ - ret =traceprobe_parse_probe_arg (arg, &tk->tp.size, parg, - flags); -if (ret) { -pr_info ("Parse error at argument[%d]. (%d)\n" , i, ret); + ret =traceprobe_parse_probe_arg (&tk->tp, i, argv[i], flags); +if (ret) goto error; - } } ret =register_trace_kprobe (tk);
kernel/trace/trace_probe.c +44 -3
65 lines not on this page · raw message
kernel/trace/trace_probe.h +2 -5
@@ -272,11 +272,8 @@ find_event_file_link(struct trace_probe *tp, struct trace_event_file *file)#define TPARG_FL_FENTRY BIT(2)#define TPARG_FL_MASK GENMASK(2, 0) -extern int traceprobe_parse_probe_arg (char *arg,ssize_t *size, -struct probe_arg *parg,unsigned int flags); - -extern int traceprobe_conflict_field_name (const char *name, -struct probe_arg *args,int narg); +extern int traceprobe_parse_probe_arg (struct trace_probe *tp,int i, +char *arg,unsigned int flags);extern int traceprobe_update_arg (struct probe_arg *arg);extern void traceprobe_free_probe_arg (struct probe_arg *arg);
kernel/trace/trace_uprobe.c +2 -42
53 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 23/30 tracing: Add unified dynamic event framework
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Add unified dynamic event framework for ftrace kprobes, uprobes and synthetic events. Those dynamic events can be co-exist on same file because those syntax doesn't overlap.
This introduces a framework part which provides a unified tracefs interface and operations.
--- kernel/trace/Kconfig | 3 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 4 + kernel/trace/trace_dynevent.c | 210 ++++++++++++++++++++++++++++++++++ kernel/trace/trace_dynevent.h | 119 +++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 kernel/trace/trace_dynevent.c create mode 100644 kernel/trace/trace_dynevent.h
kernel/trace/Kconfig +3 -0
@@ -518,6 +518,9 @@ config BPF_EVENTS help This allows the user to attach BPF programs to kprobe events. +config DYNAMIC_EVENTS + def_bool n + config PROBE_EVENTS def_bool n
kernel/trace/Makefile +1 -0
@@ -79,6 +79,7 @@ endif ifeq ($(CONFIG_TRACING),y) obj-$(CONFIG_KGDB_KDB) += trace_kdb.o endif +obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o
kernel/trace/trace.c +4 -0
@@ -4604,6 +4604,10 @@ static const char readme_msg[] = "\t\t\t traces\n"#endif #endif /* CONFIG_STACK_TRACER */ +#ifdef CONFIG_DYNAMIC_EVENTS +" dynamic_events\t\t- Add/remove/show the generic dynamic events\n" + "\t\t\t Write into this file to define/undefine new trace events.\n" +#endif #ifdef CONFIG_KPROBE_EVENTS " kprobe_events\t\t- Add/remove/show the kernel dynamic events\n" "\t\t\t Write into this file to define/undefine new trace events.\n"
kernel/trace/trace_dynevent.c new file +210 -0
210 lines not on this page · raw message
kernel/trace/trace_dynevent.h new file +119 -0
119 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 24/30 tracing/kprobes: Use dyn_event framework for kprobe events
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Use dyn_event framework for kprobe events. This shows kprobe events on "tracing/dynamic_events" file.
User can also define new events via tracing/dynamic_events.
--- Documentation/trace/kprobetrace.rst | 3 + kernel/trace/Kconfig | 1 + kernel/trace/trace_kprobe.c | 319 +++++++++++++++------------- kernel/trace/trace_probe.c | 27 +++ kernel/trace/trace_probe.h | 2 + 5 files changed, 207 insertions(+), 145 deletions(-)
Documentation/trace/kprobetrace.rst +3 -0
@@ -20,6 +20,9 @@ current_tracer. Instead of that, add probe points via /sys/kernel/debug/tracing/kprobe_events, and enable it via /sys/kernel/debug/tracing/events/kprobes/<EVENT>/enable. +You can also use /sys/kernel/debug/tracing/dynamic_events instead of +kprobe_events. That interface will provide unified access to other +dynamic events too. Synopsis of kprobe_events -------------------------
kernel/trace/Kconfig +1 -0
@@ -461,6 +461,7 @@ config KPROBE_EVENTS bool "Enable kprobes-based dynamic events" select TRACING select PROBE_EVENTS + select DYNAMIC_EVENTS default y help This allows the user to add tracing events (similar to tracepoints)
kernel/trace/trace_kprobe.c +174 -145
591 lines not on this page · raw message
kernel/trace/trace_probe.c +27 -0
@@ -154,6 +154,33 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset)return 0 ; } +/* @buf must has MAX_EVENT_NAME_LEN size */ +int traceprobe_parse_event_name (const char **pevent,const char **pgroup, +char *buf) +{ +const char *slash, *event = *pevent; + + slash =strchr (event,'/' ); +if (slash) { +if (slash == event) { +pr_info ("Group name is not specified\n" ); +return -EINVAL ; + } +if (slash - event +1 >MAX_EVENT_NAME_LEN ) { +pr_info ("Group name is too long\n" ); +return -E2BIG ; + } +strlcpy (buf, event, slash - event +1 ); + *pgroup = buf; + *pevent = slash +1 ; + } +if (strlen (event) ==0 ) { +pr_info ("Event name is not specified\n" ); +return -EINVAL ; + } +return 0 ; +} +#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) static int parse_probe_vars(char *arg, const struct fetch_type *t,
kernel/trace/trace_probe.h +2 -0
@@ -279,6 +279,8 @@ extern int traceprobe_update_arg(struct probe_arg *arg);extern void traceprobe_free_probe_arg (struct probe_arg *arg);extern int traceprobe_split_symbol_offset (char *symbol,long *offset); +extern int traceprobe_parse_event_name (const char **pevent, +const char **pgroup,char *buf);extern int traceprobe_set_print_fmt (struct trace_probe *tp,bool is_return);
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 25/30 tracing/uprobes: Use dyn_event framework for uprobe events
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Use dyn_event framework for uprobe events. This shows uprobe events on "dynamic_events" file.
User can also define new uprobe events via dynamic_events.
--- Documentation/trace/uprobetracer.rst | 4 + kernel/trace/Kconfig | 1 + kernel/trace/trace_uprobe.c | 278 ++++++++++++++------------- 3 files changed, 153 insertions(+), 130 deletions(-)
Documentation/trace/uprobetracer.rst +4 -0
@@ -18,6 +18,10 @@ current_tracer. Instead of that, add probe points via However unlike kprobe-event tracer, the uprobe event interface expects the user to calculate the offset of the probepoint in the object. +You can also use /sys/kernel/debug/tracing/dynamic_events instead of +uprobe_events. That interface will provide unified access to other +dynamic events too. + Synopsis of uprobe_tracer ------------------------- ::
kernel/trace/Kconfig +1 -0
@@ -501,6 +501,7 @@ config UPROBE_EVENTS depends on PERF_EVENTS select UPROBES select PROBE_EVENTS + select DYNAMIC_EVENTS select TRACING default y help
kernel/trace/trace_uprobe.c +148 -130
498 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 26/30 tracing: Use dyn_event framework for synthetic events
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Use dyn_event framework for synthetic events. This shows synthetic events on "tracing/dynamic_events" file in addition to tracing/synthetic_events interface.
User can also define new events via tracing/dynamic_events with "s:" prefix. So, the new syntax is below;
s:[synthetic/]EVENT_NAME TYPE ARG; [TYPE ARG;]...
To remove events via tracing/dynamic_events, you can use "-:" prefix as same as other events.
--- kernel/trace/Kconfig | 1 + kernel/trace/trace.c | 8 + kernel/trace/trace_events_hist.c | 265 +++++++++++++++++++------------ 3 files changed, 176 insertions(+), 98 deletions(-)
kernel/trace/Kconfig +1 -0
@@ -635,6 +635,7 @@ config HIST_TRIGGERS depends on ARCH_HAVE_NMI_SAFE_CMPXCHG select TRACING_MAP select TRACING + select DYNAMIC_EVENTS default n help Hist triggers allow one or more arbitrary trace event fields
kernel/trace/trace.c +8 -0
@@ -4620,6 +4620,9 @@ static const char readme_msg[] = "\t accepts: event-definitions (one definition per line)\n" "\t Format: p[:[<group>/]<event>] <place> [<args>]\n" "\t r[maxactive][:[<group>/]<event>] <place> [<args>]\n" +#ifdef CONFIG_HIST_TRIGGERS + "\t s:[synthetic/]<event> <field> [<field>]\n" +#endif "\t -:[<group>/]<event>\n"#ifdef CONFIG_KPROBE_EVENTS "\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n"
@@ -4638,6 +4641,11 @@ static const char readme_msg[] ="\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" "\t b<bit-width>@<bit-offset>/<container-size>,\n" "\t <type>\\[<array-size>\\]\n" +#ifdef CONFIG_HIST_TRIGGERS +"\t field: <stype> <name>;\n" +"\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" + "\t [unsigned] char/int/long\n" +#endif #endif " events/\t\t- Directory containing all trace event subsystems:\n" " enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
kernel/trace/trace_events_hist.c +167 -98
441 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 27/30 tracing: Remove unneeded synth_event_mutex
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Rmove unneeded synth_event_mutex. This mutex protects the reference count in synth_event, however, those operational points are already protected by event_mutex.
1. In __create_synth_event() and create_or_delete_synth_event(), those synth_event_mutex clearly obtained right after event_mutex.
2. event_hist_trigger_func() is trigger_hist_cmd.func() which is called by trigger_process_regex(), which is a part of event_trigger_regex_write() and this function takes event_mutex.
3. hist_unreg_all() is trigger_hist_cmd.unreg_all() which is called by event_trigger_regex_open() and it takes event_mutex.
4. onmatch_destroy() and onmatch_create() have long call tree, but both are finally invoked from event_trigger_regex_write() and event_trace_del_tracer(), former takes event_mutex, and latter ensures called under event_mutex locked.
Finally, I ensured there is no resource conflict. For safety, I added lockdep_assert_held(&event_mutex) for each function.
--- kernel/trace/trace_events_hist.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-)
kernel/trace/trace_events_hist.c +7 -23
@@ -444,8 +444,6 @@ static bool have_hist_err(void)return false; } -static DEFINE_MUTEX (synth_event_mutex); -struct synth_trace_event {struct trace_entry ent;u64 fields[];
@@ -1077,7 +1075,6 @@ static int __create_synth_event(int argc, const char *name, const char **argv)return -EINVAL ;mutex_lock (&event_mutex); -mutex_lock (&synth_event_mutex); event =find_synth_event (name);if (event) {
@@ -1119,7 +1116,6 @@ static int __create_synth_event(int argc, const char *name, const char **argv)else free_synth_event (event); out: -mutex_unlock (&synth_event_mutex);mutex_unlock (&event_mutex);return ret;
@@ -1139,7 +1135,6 @@ static int create_or_delete_synth_event(int argc, char **argv)/* trace_run_command() ensures argc != 0 */ if (name[0 ] =='!' ) {mutex_lock (&event_mutex); -mutex_lock (&synth_event_mutex); event =find_synth_event (name +1 );if (event) {if (event->ref)
@@ -1153,7 +1148,6 @@ static int create_or_delete_synth_event(int argc, char **argv) } }else ret = -ENOENT ; -mutex_unlock (&synth_event_mutex);mutex_unlock (&event_mutex);return ret; }
@@ -3535,7 +3529,7 @@ static void onmatch_destroy(struct action_data *data) {unsigned int i; -mutex_lock (&synth_event_mutex); +lockdep_assert_held (&event_mutex);kfree (data->onmatch.match_event);kfree (data->onmatch.match_event_system);
@@ -3548,8 +3542,6 @@ static void onmatch_destroy(struct action_data *data) data->onmatch.synth_event->ref--;kfree (data); - -mutex_unlock (&synth_event_mutex); }static void destroy_field_var (struct field_var *field_var)
@@ -3700,15 +3692,14 @@ static int onmatch_create(struct hist_trigger_data *hist_data,struct synth_event *event;int ret =0 ; -mutex_lock (&synth_event_mutex); +lockdep_assert_held (&event_mutex); + event =find_synth_event (data->onmatch.synth_event_name);if (!event) {hist_err ("onmatch: Couldn't find synthetic event: " , data->onmatch.synth_event_name); -mutex_unlock (&synth_event_mutex);return -EINVAL ; } event->ref++; -mutex_unlock (&synth_event_mutex); var_ref_idx = hist_data->n_var_refs;
@@ -3782,9 +3773,7 @@ static int onmatch_create(struct hist_trigger_data *hist_data, out:return ret; err: -mutex_lock (&synth_event_mutex); event->ref--; -mutex_unlock (&synth_event_mutex); goto out; }
@@ -5492,6 +5481,8 @@ static void hist_unreg_all(struct trace_event_file *file)struct synth_event *se;const char *se_name; +lockdep_assert_held (&event_mutex); +if (hist_file_check_refs (file))return ;
@@ -5501,12 +5492,10 @@ static void hist_unreg_all(struct trace_event_file *file)list_del_rcu (&test->list);trace_event_trigger_enable_disable (file,0 ); -mutex_lock (&synth_event_mutex); se_name =trace_event_name (file->event_call); se =find_synth_event (se_name);if (se) se->ref--; -mutex_unlock (&synth_event_mutex);update_cond_flag (file);if (hist_data->enable_timestamps)
@@ -5532,6 +5521,8 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,char *trigger, *p;int ret =0 ; +lockdep_assert_held (&event_mutex); +if (glob &&strlen (glob)) {last_cmd_set (param);hist_err_clear ();
@@ -5622,14 +5613,10 @@ static int event_hist_trigger_func(struct event_command *cmd_ops, } cmd_ops->unreg (glob+1 , trigger_ops, trigger_data, file); - -mutex_lock (&synth_event_mutex); se_name =trace_event_name (file->event_call); se =find_synth_event (se_name);if (se) se->ref--; -mutex_unlock (&synth_event_mutex); - ret =0 ; goto out_free; }
@@ -5665,13 +5652,10 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,if (ret) goto out_unreg; -mutex_lock (&synth_event_mutex); se_name =trace_event_name (file->event_call); se =find_synth_event (se_name);if (se) se->ref++; -mutex_unlock (&synth_event_mutex); -/* Just return zero, not the number of registered triggers */ ret =0 ; out:
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 28/30 tracing: Consolidate trace_add/remove_event_call back to the nolock functions
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
The trace_add/remove_event_call_nolock() functions were added to allow the tace_add/remove_event_call() code be called when the event_mutex lock was already taken. Now that all callers are done within the event_mutex, there's no reason to have two different interfaces.
Remove the current wrapper trace_add/remove_event_call()s and rename the _nolock versions back to the original names.
--- include/linux/trace_events.h | 2 -- kernel/trace/trace_events.c | 30 ++++-------------------------- kernel/trace/trace_events_hist.c | 6 +++--- kernel/trace/trace_kprobe.c | 4 ++-- kernel/trace/trace_uprobe.c | 4 ++-- 5 files changed, 11 insertions(+), 35 deletions(-)
include/linux/trace_events.h +0 -2
@@ -529,8 +529,6 @@ extern int trace_event_raw_init(struct trace_event_call *call);extern int trace_define_field (struct trace_event_call *call,const char *type,const char *name,int offset,int size,int is_signed,int filter_type); -extern int trace_add_event_call_nolock (struct trace_event_call *call); -extern int trace_remove_event_call_nolock (struct trace_event_call *call);extern int trace_add_event_call (struct trace_event_call *call);extern int trace_remove_event_call (struct trace_event_call *call);extern int trace_event_get_offsets (struct trace_event_call *call);
kernel/trace/trace_events.c +4 -26
@@ -2305,7 +2305,8 @@ __trace_early_add_new_event(struct trace_event_call *call,struct ftrace_module_file_ops ;static void __add_event_to_tracers (struct trace_event_call *call); -int trace_add_event_call_nolock (struct trace_event_call *call) +/* Add an additional event_call dynamically */ +int trace_add_event_call (struct trace_event_call *call) {int ret;lockdep_assert_held (&event_mutex);
@@ -2320,17 +2321,6 @@ int trace_add_event_call_nolock(struct trace_event_call *call)return ret; } -/* Add an additional event_call dynamically */ -int trace_add_event_call (struct trace_event_call *call) -{ -int ret; - -mutex_lock (&event_mutex); - ret =trace_add_event_call_nolock (call); -mutex_unlock (&event_mutex); -return ret; -} -/* * Must be called under locking of trace_types_lock, event_mutex and * trace_event_sem.
@@ -2376,8 +2366,8 @@ static int probe_remove_event_call(struct trace_event_call *call)return 0 ; } -/* no event_mutex version */ -int trace_remove_event_call_nolock (struct trace_event_call *call) +/* Remove an event_call */ +int trace_remove_event_call (struct trace_event_call *call) {int ret;
@@ -2392,18 +2382,6 @@ int trace_remove_event_call_nolock(struct trace_event_call *call)return ret; } -/* Remove an event_call */ -int trace_remove_event_call (struct trace_event_call *call) -{ -int ret; - -mutex_lock (&event_mutex); - ret =trace_remove_event_call_nolock (call); -mutex_unlock (&event_mutex); - -return ret; -} -#define for_each_event (event, start, end) \ for (event = start; \ (unsigned long)event < (unsigned long)end; \
kernel/trace/trace_events_hist.c +3 -3
@@ -960,7 +960,7 @@ static int register_synth_event(struct synth_event *event) call->data = event; call->tp = event->tp; - ret =trace_add_event_call_nolock (call); + ret =trace_add_event_call (call);if (ret) {pr_warn ("Failed to register synthetic event: %s\n" ,trace_event_name (call));
@@ -969,7 +969,7 @@ static int register_synth_event(struct synth_event *event) ret =set_synth_event_print_fmt (call);if (ret <0 ) { -trace_remove_event_call_nolock (call); +trace_remove_event_call (call); goto err; } out:
@@ -984,7 +984,7 @@ static int unregister_synth_event(struct synth_event *event)struct trace_event_call *call = &event->call;int ret; - ret =trace_remove_event_call_nolock (call); + ret =trace_remove_event_call (call);return ret; }
kernel/trace/trace_kprobe.c +2 -2
@@ -1353,7 +1353,7 @@ static int register_kprobe_event(struct trace_kprobe *tk)kfree (call->print_fmt);return -ENODEV ; } - ret =trace_add_event_call_nolock (call); + ret =trace_add_event_call (call);if (ret) {pr_info ("Failed to register kprobe event: %s\n" ,trace_event_name (call));
@@ -1368,7 +1368,7 @@ static int unregister_kprobe_event(struct trace_kprobe *tk)int ret;/* tp->event is unregistered in trace_remove_event_call() */ - ret =trace_remove_event_call_nolock (&tk->tp.call); + ret =trace_remove_event_call (&tk->tp.call);if (!ret)kfree (tk->tp.call.print_fmt);return ret;
kernel/trace/trace_uprobe.c +2 -2
@@ -1320,7 +1320,7 @@ static int register_uprobe_event(struct trace_uprobe *tu)return -ENODEV ; } - ret =trace_add_event_call_nolock (call); + ret =trace_add_event_call (call);if (ret) { pr_info("Failed to register uprobe event: %s\n",
@@ -1337,7 +1337,7 @@ static int unregister_uprobe_event(struct trace_uprobe *tu)int ret;/* tu->event is unregistered in trace_remove_event_call() */ - ret =trace_remove_event_call_nolock (&tu->tp.call); + ret =trace_remove_event_call (&tu->tp.call);if (ret)return ret;kfree (tu->tp.call.print_fmt);
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 29/30 tracing: Add generic event-name based remove event method
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Add a generic method to remove event from dynamic event list. This is same as other system under ftrace. You just need to pass the event name with '!', e.g.
# echo p:new_grp/new_event _do_fork > dynamic_events
This creates an event, and
# echo '!p:new_grp/new_event _do_fork' > dynamic_events
Or,
# echo '!p:new_grp/new_event' > dynamic_events
will remove new_grp/new_event event.
Note that this doesn't check the event prefix (e.g. "p:") strictly, because the "group/event" name must be unique.
--- kernel/trace/trace_dynevent.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
kernel/trace/trace_dynevent.c +11 -4
@@ -37,10 +37,17 @@ int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type)char *system =NULL , *event, *p;int ret = -ENOENT ; -if (argv[0 ][1 ] !=':' ) -return -EINVAL ; +if (argv[0 ][0 ] =='-' ) { +if (argv[0 ][1 ] !=':' ) +return -EINVAL ; + event = &argv[0 ][2 ]; + }else { + event =strchr (argv[0 ],':' ); +if (!event) +return -EINVAL ; + event++; + } - event = &argv[0 ][2 ]; p =strchr (event,'/' );if (p) { system = event;
@@ -69,7 +76,7 @@ static int create_dyn_event(int argc, char **argv)struct dyn_event_operations *ops;int ret; -if (argv[0 ][0 ] =='-' ) +if (argv[0 ][0 ] =='-' || argv[0 ][0 ] =='!' )return dyn_event_release (argc, argv,NULL );mutex_lock (&dyn_event_ops_mutex);
Signature · 1 line
2.19.1
Steven Rostedt
maintainerPATCH 30/30 selftests/ftrace: Add testcases for dynamic event
In reply to Steven RostedtFrom: Masami Hiramatsu <[email protected]>
Add common testcases for dynamic_events interface.
- Add/remove kprobe events via dynamic_events
- Add/remove synthetic events via dynamic_events
- Selective clear events (clear events other interfaces)
- Genelic clear events ("!LINE" syntax)
--- .../test.d/dynevent/add_remove_kprobe.tc | 30 +++++++++++ .../test.d/dynevent/add_remove_synth.tc | 27 ++++++++++ .../test.d/dynevent/clear_select_events.tc | 50 +++++++++++++++++++ .../test.d/dynevent/generic_clear_event.tc | 49 ++++++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_kprobe.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_synth.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/clear_select_events.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/generic_clear_event.tc
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_kprobe.tc new file +30 -0
@@ -0,0 +1,30 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove kprobe events + +[ -f dynamic_events ] || exit_unsupported + +grep -q "place: \[<module>:\]<symbol>" README || exit_unsupported +grep -q "place (kretprobe): \[<module>:\]<symbol>" README || exit_unsupported + +echo 0 > events/enable +echo > dynamic_events + +PLACE=_do_fork + +echo "p:myevent1 $PLACE" >> dynamic_events +echo "r:myevent2 $PLACE" >> dynamic_events + +grep -q myevent1 dynamic_events +grep -q myevent2 dynamic_events +test -d events/kprobes/myevent1 +test -d events/kprobes/myevent2 + +echo "-:myevent2" >> dynamic_events + +grep -q myevent1 dynamic_events +! grep -q myevent2 dynamic_events + +echo > dynamic_events + +clear_trace
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_synth.tc new file +27 -0
@@ -0,0 +1,27 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove synthetic events + +[ -f dynamic_events ] || exit_unsupported + +grep -q "s:\[synthetic/\]" README || exit_unsupported + +echo 0 > events/enable +echo > dynamic_events + +echo "s:latency1 u64 lat; pid_t pid;" >> dynamic_events +echo "s:latency2 u64 lat; pid_t pid;" >> dynamic_events + +grep -q latency1 dynamic_events +grep -q latency2 dynamic_events +test -d events/synthetic/latency1 +test -d events/synthetic/latency2 + +echo "-:synthetic/latency2" >> dynamic_events + +grep -q latency1 dynamic_events +! grep -q latency2 dynamic_events + +echo > dynamic_events + +clear_trace
tools/testing/selftests/ftrace/test.d/dynevent/clear_select_events.tc new file +50 -0
50 lines not on this page · raw message
tools/testing/selftests/ftrace/test.d/dynevent/generic_clear_event.tc new file +49 -0
49 lines not on this page · raw message
Signature · 1 line
2.19.1