function_graph: Rewrite to allow multiple users
Steven Rostedt
maintainercover function_graph: Rewrite to allow multiple users
I talked with many of you at Plumbers about rewriting the function graph tracer. Well, this is it. I was originally going to produce just a proof of concept, but when I found that I had to fix a design flaw and that covered all the arch code anyway, I decided to do more of a RFC patch set.
I probably should add more comments to the code, and update the function graph design documentation, but I wanted to get this out before the US Turkey day for your enjoyment while you try to let your pants buckle again.
Why the rewrite?
Well the fuction graph tracer is arguably the strongest of the tracers. It shows both the entrance and exit of a function, can give the timings of a function, and shows the execution of the code quite nicely.
But it has one major flaw.
It can't let more than one user access it at a time. The function tracer has had that feature for years now, but due to the design of the function graph tracer it was difficult to implement. Why?
Because you must maintain the state of a three-tuple.
Task, Function, Callback
The state is determined at by the entryfunc and must be passed to the retfunc when the function being traced returns. But this is not an easy task, as that state can be different for each task, each function and each callback.
What's the solution? I use the shadow stack that is already being used to store the function return addresses.
A big thanks to Masami Hiramatsu for suggesting this idea!
For now, I only allow an 16 users of the function graph tracer at a time. That should be more than enough. I create an array of 16 fgraph_ops pointers. When a user registers their fgraph_ops to the function graph tracer, it is assigned an index into that array, which will hold a pointer to the fgraph_ops being registered.
On entry of the function, the array is iterated and each entryfunc of the fgraph_ops in the array is called. If the entryfunc returns non-zero, then the index of that fgraph_ops is pushed on the shadow stack (along with the index to the "ret_stack entry" structure, for fast access to it). If the entryfunc returns zero, then it is ignored. If at least one function returned non-zero then the return of the traced function will also be traced.
On the return of the function, the shadow stack is examined and all the indexes that were pushed on the stack is read, and each fgraph_ops retfunc is called in the reverse order.
When a fgraph_ops is unregistered, its index in the array is set to point to a "stub" fgraph_ops that holds stub functions that just return "0" for the entryfunc and does nothing for the retfunc. This is because the retfunc may be called literally days after the entryfunc is called and we want to be able to free the fgraph_ops that is unregistered.
Note, if another fgraph_ops is registered in the same location, its retfunc may be called that was set by a previous fgraph_ops. This is not a regression because that's what can happen today if you unregister a callback from the current function_graph tracer and register another one. If this is an issue, there are ways to solve it.
This patch series is based on top of the one I just sent out to fix the design flaw:
http://lkml.kernel.org/r/[email protected]
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
ftrace/fgraph-multi
Head SHA1: b177713619ad49f3dacacfcc11ed110da73ac857
Steven Rostedt (VMware) (14):
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
fgraph: Move function graph specific code into fgraph.c
fgraph: Add new fgraph_ops structure to enable function graph hooks
function_graph: Remove unused task_curr_ret_stack()
function_graph: Move ftrace_graph_get_addr() to fgraph.c
function_graph: Have profiler use new helper ftrace_graph_get_ret_stack()
function_graph: Convert ret_stack to a series of longs
function_graph: Add an array structure that will allow multiple callbacks
function_graph: Allow multiple users to attach to function graph
function_graph: Allow for more than one callback to be registered
----
arch/arm64/kernel/stacktrace.c | 3 - include/linux/ftrace.h | 42 +- include/linux/sched.h | 2 +- kernel/trace/Makefile | 1 + kernel/trace/fgraph.c | 816 +++++++++++++++++++++++++++++++++++ kernel/trace/ftrace.c | 469 ++------------------ kernel/trace/ftrace_internal.h | 75 ++++ kernel/trace/trace.h | 6 + kernel/trace/trace_functions_graph.c | 329 ++------------ kernel/trace/trace_irqsoff.c | 10 +- kernel/trace/trace_sched_wakeup.c | 10 +- kernel/trace/trace_selftest.c | 8 +- 12 files changed, 1016 insertions(+), 755 deletions(-) create mode 100644 kernel/trace/fgraph.c create mode 100644 kernel/trace/ftrace_internal.h
Steven Rostedt
maintainerRFC PATCH 1/14 fgraph: Create a fgraph.c file to store function graph infrastructure
From: "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 | 233 +++++++++++++++++++++++++++ kernel/trace/trace_functions_graph.c | 221 ------------------------- 3 files changed, 234 insertions(+), 221 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 +233 -0
233 lines not on this page · raw message
kernel/trace/trace_functions_graph.c +0 -221
233 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerRFC PATCH 2/14 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 | 6 ++++++ kernel/trace/trace_functions_graph.c | 21 +++++++++++++++++++++ 3 files changed, 27 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 +6 -0
@@ -512,6 +512,12 @@ enum {* can only be modified by current, we can reuse trace_recursion. */ TRACE_IRQ_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 +21 -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 ;
@@ -288,6 +300,11 @@ void trace_graph_return(struct ftrace_graph_ret *trace)int cpu;int pc; +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);
@@ -311,6 +328,10 @@ void set_graph_array(struct trace_array *tr)static void trace_graph_thresh_return (struct ftrace_graph_ret *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
Re: [RFC][PATCH 02/14] fgraph: Have set_graph_notrace only affect function_graph tracer
Hi Steve,
Quoted 7 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
In order to make the function graph infrastructure more generic, there can not be code specific for the
function_graphtracer in the generic code. This includes theset_graph_notracelogic, that stops all graph calls when a function in theset_graph_notraceis hit.
Quoted 6 lines
By using the
trace_recursionmask, we can use a bit in the currenttask_structto implement the notrace code, and move the logic out offgraph.cand intotrace_functions_graph.cand keeps it affecting only the tracer and not all call graph callbacks.Signed-off-by Steven Rostedt (VMware) <[email protected]>
Thanks,
Namhyung
Quoted 108 lines
--- kernel/trace/fgraph.c | 21 --------------------- kernel/trace/trace.h | 6 ++++++ kernel/trace/trace_functions_graph.c | 21 +++++++++++++++++++++ 3 files changed, 27 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 +6
-0@@ -512,6 +512,12 @@ enum {* can only be modified by current, we can reuse trace_recursion. */ TRACE_IRQ_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 +21
-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 ;@@ -288,6 +300,11 @@ void trace_graph_return(struct ftrace_graph_ret *trace)int cpu;int pc; +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);@@ -311,6 +328,10 @@ void set_graph_array(struct trace_array *tr)static void trace_graph_thresh_return (struct ftrace_graph_ret *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
maintainerRe: [RFC][PATCH 02/14] fgraph: Have set_graph_notrace only affect function_graph tracer
Quoted 1 line · On Fri, 23 Nov 2018 09:01:18 +0900 Namhyung Kim <[email protected]> wrote:
Acked-by Namhyung Kim <[email protected]>
Thanks Namhyung!
-- Steve
Re: [RFC][PATCH 02/14] fgraph: Have set_graph_notrace only affect function_graph tracer
Quoted 7 lines · Steven Rostedt
Quoted 1 line · On Fri, 23 Nov 2018 09:01:18 +0900 Namhyung Kim <[email protected]> wrote:
Acked-by Namhyung Kim <[email protected]>Thanks Namhyung!
It'd be nice if you cc me for the whole patchset (and other tracing patches) next time.
Thanks,
Namhyung
Steven Rostedt
maintainerRe: [RFC][PATCH 02/14] fgraph: Have set_graph_notrace only affect function_graph tracer
Quoted 5 lines · On Sat, 24 Nov 2018 14:49:34 +0900 Namhyung Kim <[email protected]> wrote:
Quoted 4 lines · Steven Rostedt
On Fri, 23 Nov 2018 09:01:18 +0900
Namhyung Kim <[email protected]> wrote:
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
Acked-by Namhyung Kim <[email protected]>Thanks Namhyung!
It'd be nice if you cc me for the whole patchset (and other tracing patches) next time.
Bah, Sorry, that was an oversight. You were one of the people I planned on Cc'ing the entire patch set to, but in the process of adding names I was more focused on who was at Plumbers that I talked to, and trying to remember everyone that asked to be Cc'd. I didn't mean to leave you off. That was a mistake.
-- Steve
Re: [RFC][PATCH 02/14] fgraph: Have set_graph_notrace only affect function_graph tracer
Quoted 16 lines · Steven Rostedt
Quoted 5 lines · On Sat, 24 Nov 2018 14:49:34 +0900 Namhyung Kim <[email protected]> wrote:
Quoted 4 lines · Steven Rostedt
On Fri, 23 Nov 2018 09:01:18 +0900
Namhyung Kim <[email protected]> wrote:Quoted 7 lines
Quoted 3 lines
Quoted 1 line
Acked-by Namhyung Kim <[email protected]>Thanks Namhyung!
It'd be nice if you cc me for the whole patchset (and other tracing patches) next time.
Quoted 5 lines
Bah, Sorry, that was an oversight. You were one of the people I planned on Cc'ing the entire patch set to, but in the process of adding names I was more focused on who was at Plumbers that I talked to, and trying to remember everyone that asked to be Cc'd. I didn't mean to leave you off. That was a mistake.
No problem! Always thank you for the good work. :)
Thanks,
Namhyung
Steven Rostedt
maintainerRFC PATCH 3/14 arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
The curr_ret_stack is no longer set to -1 when not tracing a function. It is now done differently, and the FTRACE_NOTRACE_DEPTH value is no longer used. Remove the reference to it.
--- 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 · 1 line
2.19.1
Re: [RFC][PATCH 03/14] arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
Hi Steve,
Quoted 5 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
The
curr_ret_stackis no longer set to -1 when not tracing a function. It is now done differently, and theFTRACE_NOTRACE_DEPTHvalue is no longer used. Remove the reference to it.
Do you have a pointer to the commit that changed that behaviour? I just want to make sure we're not missing something in our unwind_frame() code.
Quoted 13 lines
Cc Catalin Marinas <[email protected]>Cc Will Deacon <[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 63
Quoted 3 lines
arch/arm64/kernel/stacktrace.c +0
-0@@ -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 ;
Hmm, so is this code redundant too ^^ ?
arch/arm64/kernel/stacktrace.c int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) · line 66
Quoted 3 lines
arch/arm64/kernel/stacktrace.c +0
-3-if (frame->graph <-1 ) - frame->graph +=FTRACE_NOTRACE_DEPTH ; -
Do we still need to initialise frame->graph in __save_stack_trace()?
Will
Steven Rostedt
maintainerRe: [RFC][PATCH 03/14] arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
Quoted 11 lines · On Tue, 27 Nov 2018 19:31:50 +0000 Will Deacon <[email protected]> wrote:
Hi Steve,
Quoted 5 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
The
curr_ret_stackis no longer set to -1 when not tracing a function. It is now done differently, and theFTRACE_NOTRACE_DEPTHvalue is no longer used. Remove the reference to it.Do you have a pointer to the commit that changed that behaviour? I just want to make sure we're not missing something in our
unwind_frame()code.
The commit log is slightly wrong. The -1 goes away in patch 11 of this series (and this is patch 3). But the FTRACE_NOTRACE_DEPTH is going away, which is why we need this patch.
Quoted 9 lines
Quoted 9 lines
Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>Cc linux-arm-kernel at lists.infradead.orgSigned-off-by Steven Rostedt (VMware) <[email protected]>--- arch/arm64/kernel/stacktrace.c | 3 --- 1 file changed, 3 deletions(-)
Quoted 10 lines
arch/arm64/kernel/stacktrace.c int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) · line 63
Quoted 8 lines
arch/arm64/kernel/stacktrace.c +0
-0@@ -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 ;Hmm, so is this code redundant too ^^ ?
Actually, it is fine before patch 11 (which I'm only going to take the first 10 patches for the next merge window, after they are cleaned up).
Quoted 6 lines
arch/arm64/kernel/stacktrace.c int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) · line 66
Quoted 4 lines
arch/arm64/kernel/stacktrace.c +0
-3-if (frame->graph <-1 ) - frame->graph +=FTRACE_NOTRACE_DEPTH ; -Do we still need to initialise
frame->graphin__save_stack_trace()?
You are fine till patch 11, which will probably break all this (another reason why I'm not going to push that in the next merge window).
Ideally, we need to get rid of all users of curr_ret_stack outside of the function graph logic. As it's going to change drastically. We need this in order to combine function graph and kretprobes.
I'm working on a v2 that's going to be aimed at the next merge window, that will only contain patches 1-10 of this series (and some other updates). I'll Cc you on that entire set.
-- Steve
Steven Rostedt
maintainerPATCH v2 3/14 arm64: function_graph: Remove use of FTRACE_NOTRACE_DEPTH
From: "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.
---
Changes since v1: A better change log. The patch itself is the same.
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 · 1 line
2.19.1
Steven Rostedt
maintainerRFC PATCH 4/14 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
@@ -113,16 +113,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 );
@@ -191,15 +181,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;
@@ -670,10 +667,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
@@ -716,10 +709,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
maintainerRFC PATCH 5/14 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 | 74 ++++++--------------------------- kernel/trace/ftrace_internal.h | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 kernel/trace/ftrace_internal.h
kernel/trace/ftrace.c +13 -61
@@ -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;
@@ -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
maintainerRFC PATCH 6/14 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
@@ -162,6 +178,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.
@@ -191,3 +232,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
Re: [RFC][PATCH 06/14] fgraph: Move function graph specific code into fgraph.c
Quoted 7 lines · Steven Rostedt
From: "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 offtrace.cand intofgraph.cSigned-off-by Steven Rostedt (VMware) <[email protected]>
I think this patch causes a build error if CONFIG_FUNCTION_PROFILER is disabled but function graph is enabled. The following diff fixes it for me.
thanks,
- Joel
----8<------
kernel/trace/ftrace.c +9 -6
@@ -382,6 +382,15 @@ static void ftrace_update_pid_func(void)update_ftrace_function (); } +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static bool fgraph_graph_time = true; + +void ftrace_graph_graph_time_control (bool enable) +{ + fgraph_graph_time = enable; +} +#endif + #ifdef CONFIG_FUNCTION_PROFILER struct ftrace_profile { struct hlist_node node;
@@ -783,12 +792,6 @@ function_profile_call(unsigned long ip, unsigned long parent_ip, }#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static bool fgraph_graph_time = true; - -void ftrace_graph_graph_time_control (bool enable) -{ - fgraph_graph_time = enable; -}static int profile_graph_entry (struct ftrace_graph_ent *trace) {
Steven Rostedt
maintainerRe: [RFC][PATCH 06/14] fgraph: Move function graph specific code into fgraph.c
Quoted 11 lines · On Thu, 22 Nov 2018 22:11:33 -0800 Joel Fernandes <[email protected]>…
Quoted 7 lines · Steven Rostedt
From: "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 offtrace.cand intofgraph.cSigned-off-by Steven Rostedt (VMware) <[email protected]>I think this patch causes a build error if
CONFIG_FUNCTION_PROFILERis disabled but function graph is enabled. The following diff fixes it for me.
Thanks for reporting this.
Quoted 14 lines
thanks,
- Joel
----8<------
kernel/trace/ftrace.c +0
-0@@ -382,6 +382,15 @@ static void ftrace_update_pid_func(void)update_ftrace_function (); }
kernel/trace/ftrace.c static void ftrace_update_pid_func(void) · line 393
Quoted 10 lines
kernel/trace/ftrace.c +9
-0+#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static bool fgraph_graph_time = true; + +void ftrace_graph_graph_time_control (bool enable) +{ + fgraph_graph_time = enable; +} +#endif +
I think the better answer is to move it into trace_functions_graph.c.
I'll do that and give you the "reported-by".
-- Steve
Quoted 16 lines
kernel/trace/ftrace.c +0
-6#ifdef CONFIG_FUNCTION_PROFILER struct ftrace_profile { struct hlist_node node;@@ -783,12 +792,6 @@ function_profile_call(unsigned long ip, unsigned long parent_ip, }#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static bool fgraph_graph_time = true; - -void ftrace_graph_graph_time_control (bool enable) -{ - fgraph_graph_time = enable; -}static int profile_graph_entry (struct ftrace_graph_ent *trace) {
Steven Rostedt
maintainerRe: [RFC][PATCH 06/14] fgraph: Move function graph specific code into fgraph.c
Quoted 1 line · On Fri, 23 Nov 2018 12:58:34 -0500 Steven Rostedt <[email protected]> wrote:
I think the better answer is to move it into
trace_functions_graph.c.
I take that back. I think the better answer is to not call that function if the profiler is not set, nor have that option even available. Because it has no meaning without the profiler.
-- Steve
Re: [RFC][PATCH 06/14] fgraph: Move function graph specific code into fgraph.c
Quoted 8 lines · Steven Rostedt
Quoted 1 line · On Fri, 23 Nov 2018 12:58:34 -0500 Steven Rostedt <[email protected]> wrote:
I think the better answer is to move it into
trace_functions_graph.c.I take that back. I think the better answer is to not call that function if the profiler is not set, nor have that option even available. Because it has no meaning without the profiler.
Agreed, that's better. Thanks,
- Joel
Masami Hiramatsu
maintainerRe: [RFC][PATCH 06/14] fgraph: Move function graph specific code into fgraph.c
In reply to Joel FernandesQuoted 11 lines · On Thu, 22 Nov 2018 22:11:33 -0800 Joel Fernandes <[email protected]>…
Quoted 7 lines · Steven Rostedt
From: "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 offtrace.cand intofgraph.cSigned-off-by Steven Rostedt (VMware) <[email protected]>I think this patch causes a build error if
CONFIG_FUNCTION_PROFILERis disabled but function graph is enabled. The following diff fixes it for me.
Good catch, I also confirmed it.
I think we'd better to define an empty stub of ftrace_graph_graph_time_control(enable) in trace.h instead, since fgraph_graph_time is not used if CONFIG_FUNCTION_PROFILER=n.
Thank you,
Quoted 40 lines
thanks,
- Joel
----8<------
kernel/trace/ftrace.c +9
-6@@ -382,6 +382,15 @@ static void ftrace_update_pid_func(void)update_ftrace_function (); } +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static bool fgraph_graph_time = true; + +void ftrace_graph_graph_time_control (bool enable) +{ + fgraph_graph_time = enable; +} +#endif + #ifdef CONFIG_FUNCTION_PROFILER struct ftrace_profile { struct hlist_node node;@@ -783,12 +792,6 @@ function_profile_call(unsigned long ip, unsigned long parent_ip, }#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static bool fgraph_graph_time = true; - -void ftrace_graph_graph_time_control (bool enable) -{ - fgraph_graph_time = enable; -}static int profile_graph_entry (struct ftrace_graph_ent *trace) {
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerRFC PATCH 7/14 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 | 24 +++++++++++++++++------- kernel/trace/fgraph.c | 9 ++++----- kernel/trace/ftrace.c | 10 +++++++--- kernel/trace/trace_functions_graph.c | 21 ++++++++++++++++----- kernel/trace/trace_irqsoff.c | 10 +++++++--- kernel/trace/trace_sched_wakeup.c | 10 +++++++--- kernel/trace/trace_selftest.c | 8 ++++++-- 7 files changed, 64 insertions(+), 28 deletions(-)
include/linux/ftrace.h +17 -7
@@ -749,6 +749,18 @@ 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; +struct fgraph_ops __rcu *next; +unsigned long flags; +void *private; +#ifdef CONFIG_DYNAMIC_FTRACE +struct ftrace_ops_hash local_hash; +struct ftrace_ops_hash *func_hash; +#endif +}; +/* * Stack of return addresses for functions * of a thread.
@@ -792,8 +804,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 +815,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);
@@ -830,12 +841,11 @@ 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) +static inline int register_ftrace_graph (struct fgraph_ops *ops); {return -1 ; } -static inline void unregister_ftrace_graph (void ) { } +static inline void unregister_ftrace_graph (struct fgraph_ops *ops) { }static inline int task_curr_ret_stack (struct task_struct *tsk) {
kernel/trace/fgraph.c +4 -5
@@ -491,8 +491,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 ;
@@ -513,7 +512,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
@@ -521,7 +520,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 ();
@@ -531,7 +530,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
@@ -336,17 +336,25 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace)trace_graph_return (trace); } +static struct fgraph_ops funcgraph_threash_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_threash_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 ();
@@ -357,7 +365,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_threash_ops); +else +unregister_ftrace_graph (&funcgraph_ops); }static int graph_trace_update_thresh (struct trace_array *tr)
kernel/trace/trace_irqsoff.c +7 -3
@@ -457,6 +457,11 @@ EXPORT_SYMBOL_GPL(stop_critical_timings);#ifdef CONFIG_FUNCTION_TRACER static bool function_enabled; +static struct fgraph_ops fgraph_ops = { + .entryfunc = &irqsoff_graph_entry, + .retfunc = &irqsoff_graph_return, +}; +static int register_irqsoff_function (struct trace_array *tr,int graph,int set) {int ret;
@@ -466,8 +471,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);
@@ -483,7 +487,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 -3
@@ -127,6 +127,11 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip,preempt_enable_notrace (); } +static struct fgraph_ops fgraph_wakeup_ops = { + .entryfunc = &wakeup_graph_entry, + .retfunc = &wakeup_graph_return, +}; +static int register_wakeup_function (struct trace_array *tr,int graph,int set) {int ret;
@@ -136,8 +141,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);
@@ -153,7 +157,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
Re: [RFC][PATCH 07/14] fgraph: Add new fgraph_ops structure to enable function graph hooks
Quoted 159 lines · Steven Rostedt
From: "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_opshandle. When we allow more than one callback to the function graph hooks, this will let the system know which one to remove.Signed-off-by Steven Rostedt (VMware) <[email protected]>--- include/linux/ftrace.h | 24 +++++++++++++++++------- kernel/trace/fgraph.c | 9 ++++----- kernel/trace/ftrace.c | 10 +++++++--- kernel/trace/trace_functions_graph.c | 21 ++++++++++++++++----- kernel/trace/trace_irqsoff.c | 10 +++++++--- kernel/trace/trace_sched_wakeup.c | 10 +++++++--- kernel/trace/trace_selftest.c | 8 ++++++-- 7 files changed, 64 insertions(+), 28 deletions(-)include/linux/ftrace.h +17
-7@@ -749,6 +749,18 @@ 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; +struct fgraph_ops __rcu *next; +unsigned long flags; +void *private; +#ifdef CONFIG_DYNAMIC_FTRACE +struct ftrace_ops_hash local_hash; +struct ftrace_ops_hash *func_hash; +#endif +}; +/* * Stack of return addresses for functions * of a thread. @@ -792,8 +804,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 +815,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);@@ -830,12 +841,11 @@ 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) +static inline int register_ftrace_graph (struct fgraph_ops *ops); {return -1 ; } -static inline void unregister_ftrace_graph (void ) { } +static inline void unregister_ftrace_graph (struct fgraph_ops *ops) { }static inline int task_curr_ret_stack (struct task_struct *tsk) {kernel/trace/fgraph.c +4
-5@@ -491,8 +491,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 ;@@ -513,7 +512,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 @@ -521,7 +520,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 ();@@ -531,7 +530,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 +0
-0
kernel/trace/trace_functions_graph.c static void trace_graph_thresh_return(struct ftrace_graph_ret *trace) · line 339
Quoted 4 lines
kernel/trace/trace_functions_graph.c +1
-0@@ -336,17 +336,25 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace)trace_graph_return (trace); } +static struct fgraph_ops funcgraph_threash_ops = {
minor nit: should be funcgraph_thresh_ops ?
thanks,
- Joel
Steven Rostedt
maintainerRe: [RFC][PATCH 07/14] fgraph: Add new fgraph_ops structure to enable function graph hooks
Quoted 9 lines · On Thu, 22 Nov 2018 18:59:46 -0800 Joel Fernandes <[email protected]>…
Quoted 9 lines
static struct
ftrace_opsftrace_profile_ops__read_mostly= {kernel/trace/trace_functions_graph.c +0
-0@@ -336,17 +336,25 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace)trace_graph_return (trace); }
Quoted 3 lines
kernel/trace/trace_functions_graph.c static void trace_graph_thresh_return(struct ftrace_graph_ret *trace) · line 339
Quoted 1 line
kernel/trace/trace_functions_graph.c +1
-0+static struct fgraph_ops funcgraph_threash_ops = {minor nit: should be
funcgraph_thresh_ops?
At least I got it wrong consistently :-)
Fixed, thanks!
-- Steve
Masami Hiramatsu
maintainerRe: [RFC][PATCH 07/14] fgraph: Add new fgraph_ops structure to enable function graph hooks
In reply to Steven RostedtQuoted 36 lines · On Wed, 21 Nov 2018 20:27:15 -0500 Steven Rostedt <[email protected]> wrote:
From: "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_opshandle. When we allow more than one callback to the function graph hooks, this will let the system know which one to remove.Signed-off-by Steven Rostedt (VMware) <[email protected]>--- include/linux/ftrace.h | 24 +++++++++++++++++------- kernel/trace/fgraph.c | 9 ++++----- kernel/trace/ftrace.c | 10 +++++++--- kernel/trace/trace_functions_graph.c | 21 ++++++++++++++++----- kernel/trace/trace_irqsoff.c | 10 +++++++--- kernel/trace/trace_sched_wakeup.c | 10 +++++++--- kernel/trace/trace_selftest.c | 8 ++++++-- 7 files changed, 64 insertions(+), 28 deletions(-)include/linux/ftrace.h +1
-0@@ -749,6 +749,18 @@ typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */#ifdef CONFIG_FUNCTION_GRAPH_TRACER +struct fgraph_ops {
include/linux/ftrace.h typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */ · line 762
Quoted 10 lines
include/linux/ftrace.h +9
-0+trace_func_graph_ent_t entryfunc; +trace_func_graph_ret_t retfunc; +struct fgraph_ops __rcu *next; +unsigned long flags; +void *private; +#ifdef CONFIG_DYNAMIC_FTRACE +struct ftrace_ops_hash local_hash; +struct ftrace_ops_hash *func_hash; +#endif
Hmm, can we introduce these fields when we actually use it?
BTW, would you have any idea for using private field?
Thank you,
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerRe: [RFC][PATCH 07/14] fgraph: Add new fgraph_ops structure to enable function graph hooks
Quoted 12 lines · On Mon, 26 Nov 2018 20:30:49 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 11 lines
include/linux/ftrace.h +3
-0@@ -749,6 +749,18 @@ 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;
Quoted 9 lines
include/linux/ftrace.h typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */ · line 761
Quoted 7 lines
include/linux/ftrace.h +7
-0+struct fgraph_ops __rcu *next; +unsigned long flags; +void *private; +#ifdef CONFIG_DYNAMIC_FTRACE +struct ftrace_ops_hash local_hash; +struct ftrace_ops_hash *func_hash; +#endif Hmm, can we introduce these fields when we actually use it?
Sure.
Quoted 2 lines
BTW, would you have any idea for using private field?
I believe I answered this in another email.
-- Steve
Steven Rostedt
maintainerRFC PATCH 8/14 function_graph: Remove unused task_curr_ret_stack()
In reply to Steven RostedtFrom: "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
@@ -819,11 +819,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);
@@ -847,11 +842,6 @@ static inline int register_ftrace_graph(struct fgraph_ops *ops); }static inline void unregister_ftrace_graph (struct fgraph_ops *ops) { } -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
Re: [RFC][PATCH 08/14] function_graph: Remove unused task_curr_ret_stack()
Quoted 3 lines · On Wed, 21 Nov 2018 20:27:16 -0500 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
The static inline function
task_curr_ret_stack()is unused, remove it.
This looks able to be applied without this series. I think we should apply this to for-next branch?
Thanks,
Quoted 38 lines
Signed-off-by Steven Rostedt (VMware) <[email protected]>--- include/linux/ftrace.h | 10 ---------- 1 file changed, 10 deletions(-)include/linux/ftrace.h +0
-10@@ -819,11 +819,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);@@ -847,11 +842,6 @@ static inline int register_ftrace_graph(struct fgraph_ops *ops); }static inline void unregister_ftrace_graph (struct fgraph_ops *ops) { } -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
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerRe: [RFC][PATCH 08/14] function_graph: Remove unused task_curr_ret_stack()
Quoted 9 lines · On Mon, 26 Nov 2018 16:40:02 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 3 lines · On Wed, 21 Nov 2018 20:27:16 -0500 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
The static inline function
task_curr_ret_stack()is unused, remove it.This looks able to be applied without this series. I think we should apply this to for-next branch?
Well, my current "for-next" branch is really a "for-this" branch, as I will be sending it up to Linus before the new merge window.
This is a clean up that can wait till the next merge window. I'll push it to the front of the queue though, so that I can start adding some of this for the next merge window.
Quoted 3 lines
Reviewed-by Masami Hiramatsu <[email protected]>
Thanks Masami,
-- Steve
Re: [RFC][PATCH 08/14] function_graph: Remove unused task_curr_ret_stack()
In reply to Steven RostedtQuoted 1 line · Steven Rostedt
The static inline function
task_curr_ret_stack()is unused, remove it.
Just want ot make sure I understand this correctly, instead of using this function, the convention now is to just directly assign t->curr_ret_stack = -1?
Signature · 2 lines
Cheers, Joey Pabalinas
Steven Rostedt
maintainerRe: [RFC][PATCH 08/14] function_graph: Remove unused task_curr_ret_stack()
Quoted 6 lines · On Mon, 26 Nov 2018 00:02:07 -1000 Joey Pabalinas <[email protected]>…
Quoted 1 line · Steven Rostedt
The static inline function
task_curr_ret_stack()is unused, remove it.Just want ot make sure I understand this correctly, instead of using this function, the convention now is to just directly assign
t->curr_ret_stack = -1?
Not sure what you mean. This function would just return the value of curr_ret_stack, it didn't modify it.
-- Steve
Re: Re: [RFC][PATCH 08/14] function_graph: Remove unused task_curr_ret_stack()
Quoted 6 lines · Steven Rostedt
Quoted 3 lines · On Mon, 26 Nov 2018 00:02:07 -1000 Joey Pabalinas <[email protected]>…
Quoted 1 line · Steven Rostedt
The static inline function
task_curr_ret_stack()is unused, remove it.
Quoted 8 lines
Quoted 3 lines
Just want ot make sure I understand this correctly, instead of using this function, the convention now is to just directly assign
t->curr_ret_stack = -1?Not sure what you mean. This function would just return the value of
curr_ret_stack, it didn't modify it.-- Steve
Sorry for wording it a bit weird, I was mostly trying to figure out what these functions were originally used for. The uses I found in the git log were mostly things like:
ret = trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap);
I guess the 2nd argument for these trace functions are implicit now and these functions are no longer needed anywhere?
Signature · 2 lines
Cheers, Joey Pabalinas
Steven Rostedt
maintainerRFC PATCH 9/14 function_graph: Move ftrace_graph_get_addr() to fgraph.c
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Move the function function_graph_get_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
@@ -233,6 +233,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
Re: [RFC][PATCH 09/14] function_graph: Move ftrace_graph_get_addr() to fgraph.c
Quoted 5 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Move the function
function_graph_get_addr()tofgraph.c, as the management of thecurr_ret_stackis going to change, and all the accesses toret_stackneeds to be done infgraph.c.
s/ftrace_graph_get_addr/ftrace_graph_ret_addr/
thanks,
- Joel
Quoted 143 lines
Signed-off-by Steven Rostedt (VMware) <[email protected]>--- 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@@ -233,6 +233,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@@ -90,61 +90,6 @@ static void print_graph_duration(struct trace_array *tr, unsigned long long duration, struct trace_seq *s, u32 flags); -/** -* 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 */ - int __trace_graph_entry(struct trace_array *tr, struct ftrace_graph_ent *trace, unsigned long flags,Signature · 1 line
2.19.1
Steven Rostedt
maintainerRe: [RFC][PATCH 09/14] function_graph: Move ftrace_graph_get_addr() to fgraph.c
Quoted 9 lines · On Thu, 22 Nov 2018 19:13:38 -0800 Joel Fernandes <[email protected]>…
Quoted 5 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Move the function
function_graph_get_addr()tofgraph.c, as the management of thecurr_ret_stackis going to change, and all the accesses toret_stackneeds to be done infgraph.c.s/ftrace_graph_get_addr/ftrace_graph_ret_addr/
Good catch. I wrote the change logs a bit after writing the patch. I had "get" on my mind.
-- Steve
Steven Rostedt
maintainerRFC PATCH 10/14 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
@@ -792,6 +792,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
@@ -233,6 +233,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
maintainerRFC PATCH 11/14 function_graph: Convert ret_stack to a series of longs
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
In order to make it possible to have multiple callbacks registered with the function_graph tracer, the retstack needs to be converted from an array of ftrace_ret_stack structures to an array of longs. This will allow to store the list of callbacks on the stack for the return side of the functions.
[ Note, this currently breaks architectures that access the ret_stack of a
task to handle unwinding when 'return_to_handler' is on the stack ]
--- include/linux/sched.h | 2 +- kernel/trace/fgraph.c | 123 +++++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 55 deletions(-)
include/linux/sched.h +1 -1
@@ -1119,7 +1119,7 @@ struct task_struct {int curr_ret_depth;/* Stack of return addresses for return function tracing: */ -struct ftrace_ret_stack *ret_stack; +unsigned long *ret_stack;/* Timestamp for last schedule: */ unsigned long long ftrace_timestamp;
kernel/trace/fgraph.c +69 -54
306 lines not on this page · raw message
Signature · 1 line
2.19.1
Re: [RFC][PATCH 11/14] function_graph: Convert ret_stack to a series of longs
Quoted 48 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
In order to make it possible to have multiple callbacks registered with the
function_graphtracer, the retstack needs to be converted from an array offtrace_ret_stackstructures to an array of longs. This will allow to store the list of callbacks on the stack for the return side of the functions.[ Note, this currently breaks architectures that access the
ret_stackof a
task to handle unwinding when 'return_to_handler' is on the stack ]Signed-off-by Steven Rostedt (VMware) <[email protected]>--- include/linux/sched.h | 2 +- kernel/trace/fgraph.c | 123 +++++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 55 deletions(-)include/linux/sched.h +1
-1@@ -1119,7 +1119,7 @@ struct task_struct {int curr_ret_depth;/* Stack of return addresses for return function tracing: */ -struct ftrace_ret_stack *ret_stack; +unsigned long *ret_stack;/* Timestamp for last schedule: */ unsigned long long ftrace_timestamp;kernel/trace/fgraph.c +11
-0@@ -23,6 +23,17 @@#define ASSIGN_OPS_HASH (opsname, val)#endif +#define FGRAPH_RET_SIZE (sizeof(struct ftrace_ret_stack)) +#define FGRAPH_RET_INDEX (ALIGN(FGRAPH_RET_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_SIZE (FTRACE_RETFUNC_DEPTH * FGRAPH_RET_SIZE) +#define SHADOW_STACK_INDEX \ + (ALIGN(SHADOW_STACK_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_MAX_INDEX (SHADOW_STACK_INDEX - FGRAPH_RET_INDEX) + +#define RET_STACK (t, index) ((struct ftrace_ret_stack *)(&(t)->ret_stack[index])) +#define RET_STACK_INC (c) ({ c += FGRAPH_RET_INDEX; }) +#define RET_STACK_DEC (c) ({ c -= FGRAPH_RET_INDEX; }) +
[...]
Quoted 12 lines
kernel/trace/fgraph.c +1
-1@@ -514,7 +531,7 @@ void ftrace_graph_init_task(struct task_struct *t)void ftrace_graph_exit_task (struct task_struct *t) { -struct ftrace_ret_stack *ret_stack = t->ret_stack; +unsigned long *ret_stack = t->ret_stack; t->ret_stack =NULL ;/* NULL must become visible to IRQs before we free it: */ @@ -526,12 +543,10 @@ void ftrace_graph_exit_task(struct task_struct *t)/* Allocate a return stack for each task */ static int start_graph_tracing (void )
kernel/trace/fgraph.c void ftrace_graph_exit_task(struct task_struct *t) · line 550
Quoted 10 lines
kernel/trace/fgraph.c +2
-4{ -struct ftrace_ret_stack **ret_stack_list; +unsigned long **ret_stack_list;int ret, cpu; - ret_stack_list =kmalloc_array (FTRACE_RETSTACK_ALLOC_SIZE , -sizeof (struct ftrace_ret_stack *), -GFP_KERNEL ); + ret_stack_list =kmalloc (SHADOW_STACK_SIZE ,GFP_KERNEL );
I had dumped the fgraph size related macros to understand the patch better, I got:
[ 0.909528] val of FGRAPH_RET_SIZE is 40
[ 0.910250] val of FGRAPH_RET_INDEX is 5
[ 0.910866] val of FGRAPH_ARRAY_SIZE is 16
[ 0.911488] val of FGRAPH_ARRAY_MASK is 255
[ 0.912134] val of FGRAPH_MAX_INDEX is 16
[ 0.912751] val of FGRAPH_INDEX_SHIFT is 8
[ 0.913382] val of FGRAPH_FRAME_SIZE is 168
[ 0.914033] val of FGRAPH_FRAME_INDEX is 21
FTRACE_RETFUNC_DEPTH is 50
[ 0.914686] val of SHADOW_STACK_SIZE is 8400I had a concern about memory overhead per-task. It seems the total memory needed per task for the stack is 8400 bytes (with my configuration with FUNCTION_PROFILE
turned off).
Where as before it would be 32 * 40 = 1280 bytes. That looks like ~7 times more than before.
On my system with ~4000 threads, that becomes ~32MB which seems a bit wasteful especially if there was only one or 2 function graph callbacks registered and most of the callback array in the stack isn't used.
Could we make the array size configurable at compile time and start it with a small number like 4 or 6?
Also for patches 1 through 10:
thanks,
- Joel
Masami Hiramatsu
maintainerRe: [RFC][PATCH 11/14] function_graph: Convert ret_stack to a series of longs
Quoted 86 lines · On Fri, 23 Nov 2018 21:31:38 -0800 Joel Fernandes <[email protected]>…
Quoted 48 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
In order to make it possible to have multiple callbacks registered with the
function_graphtracer, the retstack needs to be converted from an array offtrace_ret_stackstructures to an array of longs. This will allow to store the list of callbacks on the stack for the return side of the functions.[ Note, this currently breaks architectures that access the
ret_stackof a
task to handle unwinding when 'return_to_handler' is on the stack ]Signed-off-by Steven Rostedt (VMware) <[email protected]>--- include/linux/sched.h | 2 +- kernel/trace/fgraph.c | 123 +++++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 55 deletions(-)include/linux/sched.h +1
-1@@ -1119,7 +1119,7 @@ struct task_struct {int curr_ret_depth;/* Stack of return addresses for return function tracing: */ -struct ftrace_ret_stack *ret_stack; +unsigned long *ret_stack;/* Timestamp for last schedule: */ unsigned long long ftrace_timestamp;kernel/trace/fgraph.c +11
-0@@ -23,6 +23,17 @@#define ASSIGN_OPS_HASH (opsname, val)#endif +#define FGRAPH_RET_SIZE (sizeof(struct ftrace_ret_stack)) +#define FGRAPH_RET_INDEX (ALIGN(FGRAPH_RET_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_SIZE (FTRACE_RETFUNC_DEPTH * FGRAPH_RET_SIZE) +#define SHADOW_STACK_INDEX \ + (ALIGN(SHADOW_STACK_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_MAX_INDEX (SHADOW_STACK_INDEX - FGRAPH_RET_INDEX) + +#define RET_STACK (t, index) ((struct ftrace_ret_stack *)(&(t)->ret_stack[index])) +#define RET_STACK_INC (c) ({ c += FGRAPH_RET_INDEX; }) +#define RET_STACK_DEC (c) ({ c -= FGRAPH_RET_INDEX; }) +[...]
Quoted 12 lines
kernel/trace/fgraph.c +1
-1@@ -514,7 +531,7 @@ void ftrace_graph_init_task(struct task_struct *t)void ftrace_graph_exit_task (struct task_struct *t) { -struct ftrace_ret_stack *ret_stack = t->ret_stack; +unsigned long *ret_stack = t->ret_stack; t->ret_stack =NULL ;/* NULL must become visible to IRQs before we free it: */ @@ -526,12 +543,10 @@ void ftrace_graph_exit_task(struct task_struct *t)/* Allocate a return stack for each task */ static int start_graph_tracing (void )kernel/trace/fgraph.c void ftrace_graph_exit_task(struct task_struct *t) · line 550
Quoted 10 lines
kernel/trace/fgraph.c +2
-4{ -struct ftrace_ret_stack **ret_stack_list; +unsigned long **ret_stack_list;int ret, cpu; - ret_stack_list =kmalloc_array (FTRACE_RETSTACK_ALLOC_SIZE , -sizeof (struct ftrace_ret_stack *), -GFP_KERNEL ); + ret_stack_list =kmalloc (SHADOW_STACK_SIZE ,GFP_KERNEL );I had dumped the fgraph size related macros to understand the patch better, I got:
[ 0.909528] val of FGRAPH_RET_SIZE is 40 [ 0.910250] val of FGRAPH_RET_INDEX is 5 [ 0.910866] val of FGRAPH_ARRAY_SIZE is 16 [ 0.911488] val of FGRAPH_ARRAY_MASK is 255 [ 0.912134] val of FGRAPH_MAX_INDEX is 16 [ 0.912751] val of FGRAPH_INDEX_SHIFT is 8 [ 0.913382] val of FGRAPH_FRAME_SIZE is 168 [ 0.914033] val of FGRAPH_FRAME_INDEX is 21 FTRACE_RETFUNC_DEPTH is 50 [ 0.914686] val of SHADOW_STACK_SIZE is 8400
Quoted 7 lines
I had a concern about memory overhead per-task. It seems the total memory needed per task for the stack is 8400 bytes (with my configuration with
FUNCTION_PROFILE
turned off).Where as before it would be 32 * 40 = 1280 bytes. That looks like ~7 times more than before.
Hmm, this seems too big... I thought the shadow-stack size should be smaller than 1 page (4kB). Steve, can we give a 4k page for shadow stack and define FTRACE_RETFUNC_DEPTH = 4096 / FGRAPH_RET_SIZE ?
Quoted 6 lines
On my system with ~4000 threads, that becomes ~32MB which seems a bit wasteful especially if there was only one or 2 function graph callbacks registered and most of the callback array in the stack isn't used.
Could we make the array size configurable at compile time and start it with a small number like 4 or 6?
Or, we can introduce online setting :)
Thank you,
Quoted 8 lines
Also for patches 1 through 10:
Reviewed-by Joel Fernandes (Google) <[email protected]>thanks,
- Joel
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerRe: [RFC][PATCH 11/14] function_graph: Convert ret_stack to a series of longs
Quoted 72 lines · On Tue, 27 Nov 2018 01:07:55 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 72 lines
Quoted 30 lines
include/linux/sched.h +1
-1@@ -1119,7 +1119,7 @@ struct task_struct {int curr_ret_depth;/* Stack of return addresses for return function tracing: */ -struct ftrace_ret_stack *ret_stack; +unsigned long *ret_stack;/* Timestamp for last schedule: */ unsigned long long ftrace_timestamp;kernel/trace/fgraph.c +11
-0@@ -23,6 +23,17 @@#define ASSIGN_OPS_HASH (opsname, val)#endif +#define FGRAPH_RET_SIZE (sizeof(struct ftrace_ret_stack)) +#define FGRAPH_RET_INDEX (ALIGN(FGRAPH_RET_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_SIZE (FTRACE_RETFUNC_DEPTH * FGRAPH_RET_SIZE) +#define SHADOW_STACK_INDEX \ + (ALIGN(SHADOW_STACK_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_MAX_INDEX (SHADOW_STACK_INDEX - FGRAPH_RET_INDEX) + +#define RET_STACK (t, index) ((struct ftrace_ret_stack *)(&(t)->ret_stack[index])) +#define RET_STACK_INC (c) ({ c += FGRAPH_RET_INDEX; }) +#define RET_STACK_DEC (c) ({ c -= FGRAPH_RET_INDEX; }) +[...]
Quoted 12 lines
kernel/trace/fgraph.c +1
-1@@ -514,7 +531,7 @@ void ftrace_graph_init_task(struct task_struct *t)void ftrace_graph_exit_task (struct task_struct *t) { -struct ftrace_ret_stack *ret_stack = t->ret_stack; +unsigned long *ret_stack = t->ret_stack; t->ret_stack =NULL ;/* NULL must become visible to IRQs before we free it: */ @@ -526,12 +543,10 @@ void ftrace_graph_exit_task(struct task_struct *t)/* Allocate a return stack for each task */ static int start_graph_tracing (void )kernel/trace/fgraph.c void ftrace_graph_exit_task(struct task_struct *t) · line 550
Quoted 10 lines
kernel/trace/fgraph.c +2
-4{ -struct ftrace_ret_stack **ret_stack_list; +unsigned long **ret_stack_list;int ret, cpu; - ret_stack_list =kmalloc_array (FTRACE_RETSTACK_ALLOC_SIZE , -sizeof (struct ftrace_ret_stack *), -GFP_KERNEL ); + ret_stack_list =kmalloc (SHADOW_STACK_SIZE ,GFP_KERNEL );I had dumped the fgraph size related macros to understand the patch better, I got:
[ 0.909528] val of FGRAPH_RET_SIZE is 40 [ 0.910250] val of FGRAPH_RET_INDEX is 5 [ 0.910866] val of FGRAPH_ARRAY_SIZE is 16 [ 0.911488] val of FGRAPH_ARRAY_MASK is 255 [ 0.912134] val of FGRAPH_MAX_INDEX is 16 [ 0.912751] val of FGRAPH_INDEX_SHIFT is 8 [ 0.913382] val of FGRAPH_FRAME_SIZE is 168 [ 0.914033] val of FGRAPH_FRAME_INDEX is 21 FTRACE_RETFUNC_DEPTH is 50 [ 0.914686] val of SHADOW_STACK_SIZE is 8400I had a concern about memory overhead per-task. It seems the total memory needed per task for the stack is 8400 bytes (with my configuration with
FUNCTION_PROFILE
turned off).
Quoted 6 lines
Quoted 2 lines
Where as before it would be 32 * 40 = 1280 bytes. That looks like ~7 times more than before.
Hmm, this seems too big... I thought the shadow-stack size should be smaller than 1 page (4kB). Steve, can we give a 4k page for shadow stack and define
FTRACE_RETFUNC_DEPTH= 4096 /FGRAPH_RET_SIZE?
For the first pass, I decided not to worry about the size. It made the code less complex :-)
Yes, I plan on working on making the size of the stack smaller, but that will probably be added on patches to do so.
Quoted 4 lines
Quoted 4 lines
On my system with ~4000 threads, that becomes ~32MB which seems a bit wasteful especially if there was only one or 2 function graph callbacks registered and most of the callback array in the stack isn't used.
Note, all 4000 threads could be doing those trace backs, and if you are doing full function graph tracing, it will use a lot.
Quoted 5 lines
Quoted 3 lines
Could we make the array size configurable at compile time and start it with a small number like 4 or 6?
Or, we can introduce online setting :)
Yes, that can easily be added. I didn't try to make this into the perfect solution, I wanted a solid one first, and then massage it into something that is more efficient, both with memory consumption and performance.
Joel and Masami, thanks for the feedback.
-- Steve
Re: [RFC][PATCH 11/14] function_graph: Convert ret_stack to a series of longs
Quoted 82 lines · Steven Rostedt
Quoted 72 lines · On Tue, 27 Nov 2018 01:07:55 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 72 lines
Quoted 30 lines
include/linux/sched.h +1
-1@@ -1119,7 +1119,7 @@ struct task_struct {int curr_ret_depth;/* Stack of return addresses for return function tracing: */ -struct ftrace_ret_stack *ret_stack; +unsigned long *ret_stack;/* Timestamp for last schedule: */ unsigned long long ftrace_timestamp;kernel/trace/fgraph.c +11
-0@@ -23,6 +23,17 @@#define ASSIGN_OPS_HASH (opsname, val)#endif +#define FGRAPH_RET_SIZE (sizeof(struct ftrace_ret_stack)) +#define FGRAPH_RET_INDEX (ALIGN(FGRAPH_RET_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_SIZE (FTRACE_RETFUNC_DEPTH * FGRAPH_RET_SIZE) +#define SHADOW_STACK_INDEX \ + (ALIGN(SHADOW_STACK_SIZE, sizeof(long)) / sizeof(long)) +#define SHADOW_STACK_MAX_INDEX (SHADOW_STACK_INDEX - FGRAPH_RET_INDEX) + +#define RET_STACK (t, index) ((struct ftrace_ret_stack *)(&(t)->ret_stack[index])) +#define RET_STACK_INC (c) ({ c += FGRAPH_RET_INDEX; }) +#define RET_STACK_DEC (c) ({ c -= FGRAPH_RET_INDEX; }) +[...]
Quoted 12 lines
kernel/trace/fgraph.c +1
-1@@ -514,7 +531,7 @@ void ftrace_graph_init_task(struct task_struct *t)void ftrace_graph_exit_task (struct task_struct *t) { -struct ftrace_ret_stack *ret_stack = t->ret_stack; +unsigned long *ret_stack = t->ret_stack; t->ret_stack =NULL ;/* NULL must become visible to IRQs before we free it: */ @@ -526,12 +543,10 @@ void ftrace_graph_exit_task(struct task_struct *t)/* Allocate a return stack for each task */ static int start_graph_tracing (void )kernel/trace/fgraph.c void ftrace_graph_exit_task(struct task_struct *t) · line 550
Quoted 10 lines
kernel/trace/fgraph.c +2
-4{ -struct ftrace_ret_stack **ret_stack_list; +unsigned long **ret_stack_list;int ret, cpu; - ret_stack_list =kmalloc_array (FTRACE_RETSTACK_ALLOC_SIZE , -sizeof (struct ftrace_ret_stack *), -GFP_KERNEL ); + ret_stack_list =kmalloc (SHADOW_STACK_SIZE ,GFP_KERNEL );I had dumped the fgraph size related macros to understand the patch better, I got:
[ 0.909528] val of FGRAPH_RET_SIZE is 40 [ 0.910250] val of FGRAPH_RET_INDEX is 5 [ 0.910866] val of FGRAPH_ARRAY_SIZE is 16 [ 0.911488] val of FGRAPH_ARRAY_MASK is 255 [ 0.912134] val of FGRAPH_MAX_INDEX is 16 [ 0.912751] val of FGRAPH_INDEX_SHIFT is 8 [ 0.913382] val of FGRAPH_FRAME_SIZE is 168 [ 0.914033] val of FGRAPH_FRAME_INDEX is 21 FTRACE_RETFUNC_DEPTH is 50 [ 0.914686] val of SHADOW_STACK_SIZE is 8400I had a concern about memory overhead per-task. It seems the total memory needed per task for the stack is 8400 bytes (with my configuration with
FUNCTION_PROFILE
turned off).Quoted 6 lines
Quoted 2 lines
Where as before it would be 32 * 40 = 1280 bytes. That looks like ~7 times more than before.
Hmm, this seems too big... I thought the shadow-stack size should be smaller than 1 page (4kB). Steve, can we give a 4k page for shadow stack and define
FTRACE_RETFUNC_DEPTH= 4096 /FGRAPH_RET_SIZE?
Quoted 5 lines
For the first pass, I decided not to worry about the size. It made the code less complex :-)
Yes, I plan on working on making the size of the stack smaller, but that will probably be added on patches to do so.
Cool, sounds good.
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
On my system with ~4000 threads, that becomes ~32MB which seems a bit wasteful especially if there was only one or 2 function graph callbacks registered and most of the callback array in the stack isn't used.
Note, all 4000 threads could be doing those trace backs, and if you are doing full function graph tracing, it will use a lot.
But I think each of the threads will only use N entries in the callback array where N is the number of function graph callback users who registered, right? So the remaining total-N allocated callback array entries per thread will not be used.
Quoted 11 lines
Quoted 4 lines
Quoted 2 lines
Could we make the array size configurable at compile time and start it with a small number like 4 or 6?
Or, we can introduce online setting :)
Yes, that can easily be added. I didn't try to make this into the perfect solution, I wanted a solid one first, and then massage it into something that is more efficient, both with memory consumption and performance.
Joel and Masami, thanks for the feedback.
I agree the first step is good so far. Looking forward to the patches, thanks a lot,
- Joel
Steven Rostedt
maintainerRe: [RFC][PATCH 11/14] function_graph: Convert ret_stack to a series of longs
In reply to Joel FernandesQuoted 2 lines · On Fri, 23 Nov 2018 21:31:38 -0800 Joel Fernandes <[email protected]>…
Also for patches 1 through 10:
Reviewed-by Joel Fernandes (Google) <[email protected]>
Thanks!
I'll move these over to my ftrace/core branch and start testing them for linux-next. The rest can probably wait till the next merge window after that.
-- Steve
Steven Rostedt
maintainerRFC PATCH 12/14 function_graph: Add an array structure that will allow multiple callbacks
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Add an array structure that will eventually allow the function graph tracer to have up to 16 simultaneous callbacks attached. It's an array of 16 fgraph_ops pointers, that is assigned when one is registered. On entry of a function the entry of the first item in the array is called, and if it returns zero, then the callback returns non zero if it wants the return callback to be called on exit of the function.
The array will simplify the process of having more than one callback attached to the same function, as its index into the array can be stored on the shadow stack. We need to only save the index, because this will allow the fgraph_ops to be freed before the function returns (which may happen if the function call schedule for a long time).
--- kernel/trace/fgraph.c | 48 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-)
kernel/trace/fgraph.c +43 -5
@@ -37,6 +37,14 @@static bool kill_ftrace_graph;int ftrace_graph_active; +#define FGRAPH_ARRAY_SIZE 16 + +#define SHADOW_STACK_SIZE (FTRACE_RETFUNC_DEPTH * \ + (sizeof(struct ftrace_ret_stack) + \ + FGRAPH_ARRAY_SIZE * sizeof(long))) + +static struct fgraph_ops *fgraph_array[FGRAPH_ARRAY_SIZE ]; +/* Both enabled by default (can be cleared by function_graph tracer flags */ static bool fgraph_sleep_time = true;
@@ -123,7 +131,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, goto out;/* Only trace if the calling function expects to */ -if (!ftrace_graph_entry (&trace)) +if (!fgraph_array[0 ]->entryfunc (&trace)) goto out_ret;return 0 ;
@@ -231,7 +239,7 @@ unsigned long ftrace_return_to_handler(unsigned long frame_pointer)ftrace_pop_return_trace (&trace, &ret, frame_pointer); trace.rettime =trace_clock_local (); -ftrace_graph_return (&trace); + fgraph_array[0 ]->retfunc (&trace);/* * The ftrace_graph_return() may still access the current * ret_stack structure, we need to make sure the update of
@@ -343,6 +351,11 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)return 0 ; } +static struct fgraph_ops fgraph_stub = { + .entryfunc = ftrace_graph_entry_stub, + .retfunc = (trace_func_graph_ret_t )ftrace_stub, +}; +/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = (trace_func_graph_ret_t )ftrace_stub;
@@ -575,15 +588,29 @@ static int start_graph_tracing(void)int register_ftrace_graph (struct fgraph_ops *gops) {int ret =0 ; +int i;mutex_lock (&ftrace_lock); -/* we currently allow only one tracer registered at a time */ -if (ftrace_graph_active) { +if (!fgraph_array[0 ]) { +/* The array must always have real data on it */ +for (i =0 ; i <FGRAPH_ARRAY_SIZE ; i++) { + fgraph_array[i] = &fgraph_stub; + } + } + +/* Look for an available spot */ +for (i =0 ; i <FGRAPH_ARRAY_SIZE ; i++) { +if (fgraph_array[i] == &fgraph_stub) +break ; + } +if (i >=FGRAPH_ARRAY_SIZE ) { ret = -EBUSY ; goto out; } + fgraph_array[i] = gops; +register_pm_notifier (&ftrace_suspend_notifier); ftrace_graph_active++;
@@ -594,7 +621,6 @@ int register_ftrace_graph(struct fgraph_ops *gops) } ftrace_graph_return = gops->retfunc; -/* * Update the indirect function to the entryfunc, and the * function that gets called to the entry_test first. Then
@@ -613,11 +639,23 @@ int register_ftrace_graph(struct fgraph_ops *gops)void unregister_ftrace_graph (struct fgraph_ops *gops) { +int i; +mutex_lock (&ftrace_lock);if (unlikely (!ftrace_graph_active)) goto out; +/* Look for an available spot */ +for (i =0 ; i <FGRAPH_ARRAY_SIZE ; i++) { +if (fgraph_array[i] == gops) +break ; + } +if (i ==FGRAPH_ARRAY_SIZE ) + goto out; + + fgraph_array[i] = &fgraph_stub; + ftrace_graph_active--; ftrace_graph_return = (trace_func_graph_ret_t )ftrace_stub; ftrace_graph_entry = ftrace_graph_entry_stub;
Signature · 1 line
2.19.1
Steven Rostedt
maintainerRFC PATCH 13/14 function_graph: Allow multiple users to attach to function graph
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Allow for multiple users to attach to function graph tracer at the same time. Only 16 simultaneous users can attach to the tracer. This is because there's an array that stores the pointers to the attached fgraph_ops. When a a function being traced is entered, each of the ftrace_ops entryfunc is called and if it returns non zero, its index into the array will be added to the shadow stack.
On exit of the function being traced, the shadow stack will contain the indexes of the ftrace_ops on the array that want their retfunc to be called.
Because a function may sleep for a long time (if a task sleeps itself), the return of the function may be literally days later. If the ftrace_ops is removed, its place on the array is replaced with a ftrace_ops that contains the stub functions and that will be called when the function finally returns.
If another ftrace_ops is added that happens to get the same index into the array, its return function may be called. But that's actually the way things current work with the old function graph tracer. If one tracer is removed and another is added, the new one will get the return calls of the function traced by the previous one, thus this is not a regression.
Note, being able to filter functions when both are called is not completely handled yet, but that shouldn't be too hard to manage.
--- include/linux/ftrace.h | 4 + kernel/trace/fgraph.c | 261 +++++++++++++++++++++++++++++++---------- 2 files changed, 206 insertions(+), 59 deletions(-)
include/linux/ftrace.h +4 -0
@@ -779,6 +779,8 @@ struct ftrace_ret_stack {#ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR unsigned long *retp;#endif +unsigned long callers; +unsigned long calls[]; };/*
@@ -798,6 +800,8 @@ 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); +int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp);/* * Sometimes we don't want to trace a function with the function * graph tracer but we want them to keep traced by the usual function
kernel/trace/fgraph.c +202 -59
419 lines not on this page · raw message
Signature · 1 line
2.19.1
Steven Rostedt
maintainerRFC PATCH 14/14 function_graph: Allow for more than one callback to be registered
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
--- kernel/trace/fgraph.c | 50 +++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 23 deletions(-)
kernel/trace/fgraph.c +27 -23
@@ -753,24 +753,27 @@ int register_ftrace_graph(struct fgraph_ops *gops)register_pm_notifier (&ftrace_suspend_notifier); ftrace_graph_active++; - ret =start_graph_tracing (); -if (ret) { - ftrace_graph_active--; - goto out; - } +if (ftrace_graph_active ==1 ) { + ret =start_graph_tracing (); +if (ret) { + ftrace_graph_active--; + goto out; + } - ftrace_graph_return = gops->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 = gops->entryfunc; - ftrace_graph_entry = ftrace_graph_entry_test; -update_function_graph_func (); + ftrace_graph_return = gops->retfunc; - ret =ftrace_startup (&graph_ops,FTRACE_START_FUNC_RET ); +/* +* 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 = gops->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;
@@ -800,13 +803,14 @@ void unregister_ftrace_graph(struct fgraph_ops *gops) } 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 ); - +if (ftrace_graph_active ==0 ) { + 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); }
Signature · 1 line
2.19.1
Peter Zijlstra
maintainerQuoted 7 lines · Steven Rostedt
Well the fuction graph tracer is arguably the strongest of the tracers. It shows both the entrance and exit of a function, can give the timings of a function, and shows the execution of the code quite nicely.
But it has one major flaw.
It can't let more than one user access it at a time.
The reason I 'never' use it is performance, it _sucks_.. I've never ran into the multi-user issue.
So while I don't think the rewrite is bad, this argument here is.
Steven Rostedt
maintainerQuoted 11 lines · On Thu, 22 Nov 2018 11:08:12 +0100 Peter Zijlstra <[email protected]> wrote:
Quoted 7 lines · Steven Rostedt
Well the fuction graph tracer is arguably the strongest of the tracers. It shows both the entrance and exit of a function, can give the timings of a function, and shows the execution of the code quite nicely.
But it has one major flaw.
It can't let more than one user access it at a time.
The reason I 'never' use it is performance, it _sucks_.. I've never ran into the multi-user issue.
And performance is also something to fix (it has improved lately, you probably haven't noticed).
Quoted 2 lines
So while I don't think the rewrite is bad, this argument here is.
Except that we plan on merging kretprobe with function graph tracing. This also solves the issue that Mark Rutland has with ret protection. He has a solution for function graph tracing, but not with kretprobes.
And yes, there's also the case of being able to trace to different buffers where you can have a full function graph tracing enabled and also trace a subset that you want to have.
Just because you don't need it, doesn't mean it's not needed by others.
-- Steve
Peter Zijlstra
maintainerQuoted 10 lines · Steven Rostedt
Quoted 7 lines · On Thu, 22 Nov 2018 11:08:12 +0100 Peter Zijlstra <[email protected]> wrote:
Quoted 6 lines · Steven Rostedt
Well the fuction graph tracer is arguably the strongest of the tracers. It shows both the entrance and exit of a function, can give the timings of a function, and shows the execution of the code quite nicely.
But it has one major flaw.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
It can't let more than one user access it at a time.
The reason I 'never' use it is performance, it _sucks_.. I've never ran into the multi-user issue.
And performance is also something to fix (it has improved lately, you probably haven't noticed).
Indeed, I've not recently used it.
Quoted 5 lines
Quoted 1 line
So while I don't think the rewrite is bad, this argument here is.
Except that we plan on merging kretprobe with function graph tracing. This also solves the issue that Mark Rutland has with ret protection. He has a solution for function graph tracing, but not with kretprobes.
Sure. But you didn't make that argument.
Quoted 5 lines
And yes, there's also the case of being able to trace to different buffers where you can have a full function graph tracing enabled and also trace a subset that you want to have.
Just because you don't need it, doesn't mean it's not needed by others.
Not saying that; just saying the argument doesn't hold. It is not the _one_ flaw.
Masami Hiramatsu
maintainerHi Steve,
Quoted 6 lines · On Wed, 21 Nov 2018 20:27:08 -0500 Steven Rostedt <[email protected]> wrote:
I talked with many of you at Plumbers about rewriting the function graph tracer. Well, this is it. I was originally going to produce just a proof of concept, but when I found that I had to fix a design flaw and that covered all the arch code anyway, I decided to do more of a RFC patch set.
Thank you for starting this work! This might be a good way to simplify treating of shadow stacks in kretprobe and function-graph-tracers. (And I hope this can help me to remove some kind of complexity in
kretprobes)
Quoted 5 lines
I probably should add more comments to the code, and update the function graph design documentation, but I wanted to get this out before the US Turkey day for your enjoyment while you try to let your pants buckle again.
:)
Let me try to review and port kretprobe on it. On the way, I will find issues if there are.
Quoted 51 lines
Why the rewrite?
Well the fuction graph tracer is arguably the strongest of the tracers. It shows both the entrance and exit of a function, can give the timings of a function, and shows the execution of the code quite nicely.
But it has one major flaw.
It can't let more than one user access it at a time. The function tracer has had that feature for years now, but due to the design of the function graph tracer it was difficult to implement. Why?
Because you must maintain the state of a three-tuple.
Task, Function, Callback
The state is determined at by the entryfunc and must be passed to the retfunc when the function being traced returns. But this is not an easy task, as that state can be different for each task, each function and each callback.
What's the solution? I use the shadow stack that is already being used to store the function return addresses.
A big thanks to Masami Hiramatsu for suggesting this idea!
For now, I only allow an 16 users of the function graph tracer at a time. That should be more than enough. I create an array of 16
fgraph_opspointers. When a user registers theirfgraph_opsto the function graph tracer, it is assigned an index into that array, which will hold a pointer to thefgraph_opsbeing registered.On entry of the function, the array is iterated and each entryfunc of the
fgraph_opsin the array is called. If the entryfunc returns non-zero, then the index of thatfgraph_opsis pushed on the shadow stack (along with the index to the "ret_stackentry" structure, for fast access to it). If the entryfunc returns zero, then it is ignored. If at least one function returned non-zero then the return of the traced function will also be traced.On the return of the function, the shadow stack is examined and all the indexes that were pushed on the stack is read, and each
fgraph_opsretfunc is called in the reverse order.When a
fgraph_opsis unregistered, its index in the array is set to point to a "stub"fgraph_opsthat holds stub functions that just return "0" for the entryfunc and does nothing for the retfunc. This is because the retfunc may be called literally days after the entryfunc is called and we want to be able to free thefgraph_opsthat is unregistered.
Quoted 5 lines
Note, if another
fgraph_opsis registered in the same location, its retfunc may be called that was set by a previousfgraph_ops. This is not a regression because that's what can happen today if you unregister a callback from the currentfunction_graphtracer and register another one. If this is an issue, there are ways to solve it.
Yeah, I need the solution, maybe an API to get correct return address? :)
By the way, are there any way to hold a private data on each ret_stack entry? Since kretprobe supports "entry data" passed from entry_handler to return handler, we have to store the data or data-instance on the ret_stack.
This feature is used by systemtap to save the function entry data, like function parameters etc., so that return handler analyzes the parameters with return value.
Thank you,
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerQuoted 7 lines · On Mon, 26 Nov 2018 18:21:12 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 5 lines
Note, if another
fgraph_opsis registered in the same location, its retfunc may be called that was set by a previousfgraph_ops. This is not a regression because that's what can happen today if you unregister a callback from the currentfunction_graphtracer and register another one. If this is an issue, there are ways to solve it.Yeah, I need the solution, maybe an API to get correct return address? :)
One way to solve this is to also have a counter array that gets updated every time the index array gets updated. And save the counter to the shadow stack index as well. This way, we only call the return if the counter on the stack matches what's in the counter on the counter array for the index.
Quoted 8 lines
By the way, are there any way to hold a private data on each
ret_stackentry? Since kretprobe supports "entry data" passed fromentry_handlerto return handler, we have to store the data or data-instance on theret_stack.This feature is used by systemtap to save the function entry data, like function parameters etc., so that return handler analyzes the parameters with return value.
Yes, I remember you telling me about this at plumbers, and while I was writing this code I had that in mind. It wouldn't be too hard to implement, I just left it off for now. I also left it off because I have some questions about what exactly is needed. What size do you require to be stored. Especially if we want to keep the shadow stack smaller. I was going to find a way to implement some of the data that is already stored via the ret_stack with this instead, and make the ret_stack entry smaller. Should we allow just sizeof(long)*3? or just let user choose any size and if they run out of stack, then too bad. We just wont let it crash.
-- Steve
Masami Hiramatsu
maintainerHi Steve,
Quoted 10 lines · On Mon, 26 Nov 2018 11:32:15 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 6 lines · On Mon, 26 Nov 2018 18:21:12 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 5 lines
Note, if another
fgraph_opsis registered in the same location, its retfunc may be called that was set by a previousfgraph_ops. This is not a regression because that's what can happen today if you unregister a callback from the currentfunction_graphtracer and register another one. If this is an issue, there are ways to solve it.
Quoted 7 lines
Quoted 1 line
Yeah, I need the solution, maybe an API to get correct return address? :)
One way to solve this is to also have a counter array that gets updated every time the index array gets updated. And save the counter to the shadow stack index as well. This way, we only call the return if the counter on the stack matches what's in the counter on the counter array for the index.
Hmm, but we already know the current stack "header" entry when calling handlers, don't we? I thought we just calcurate out from curr_ret_stack.
Quoted 8 lines
Quoted 7 lines
By the way, are there any way to hold a private data on each
ret_stackentry? Since kretprobe supports "entry data" passed fromentry_handlerto return handler, we have to store the data or data-instance on theret_stack.This feature is used by systemtap to save the function entry data, like function parameters etc., so that return handler analyzes the parameters with return value.
Quoted 10 lines
Yes, I remember you telling me about this at plumbers, and while I was writing this code I had that in mind. It wouldn't be too hard to implement, I just left it off for now. I also left it off because I have some questions about what exactly is needed. What size do you require to be stored. Especially if we want to keep the shadow stack smaller. I was going to find a way to implement some of the data that is already stored via the
ret_stackwith this instead, and make theret_stackentry smaller. Should we allow just sizeof(long)*3? or just let user choose any size and if they run out of stack, then too bad. We just wont let it crash.
I need only sizeof(unsigned long). If the kretprobe user requires more, it will be fall back to current method -- get an "instance" and store its address to the entry :-)
Thank you,
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerQuoted 8 lines · On Thu, 29 Nov 2018 23:29:27 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 5 lines
One way to solve this is to also have a counter array that gets updated every time the index array gets updated. And save the counter to the shadow stack index as well. This way, we only call the return if the counter on the stack matches what's in the counter on the counter array for the index.
Hmm, but we already know the current stack "header" entry when calling handlers, don't we? I thought we just calcurate out from
curr_ret_stack.
Basically we have this:
array: | &fgraph_ops_1 | &fgraph_ops_2 | &fgraph_ops_stub | ...
On entry of function we do:
for (i = 0; i < array_entries; i++) {
if (array[i]->entryfunc(...)) {
push i onto ret_stack;
}
}On the return side, we do:
idx = pop ret_stack; array[idx]->retfunc(...);
We only call the retfunc of a fgraph_ops if it returned non-zero from its entryfunc(). The return can happen a long time from now (which is why I don't save the &fgraph_ops on the ret_stack, because then we would never be able to free it).
In the mean time, lets say we unregistered (and freed) fgraph_ops_2 and then added fgraph_ops_3, so the array looks like:
array: | &fgraph_ops_1 | &fgraph_ops_3 | &fgraph_ops_stub | ...
Then a function that was called when fgraph_ops_2 was on the stack returns, it will call array[1]->retfunc() which now belongs to fgraph_ops_3 and not fgraph_ops_2.
But if we add a counter array that gets updated when new ops are added to the array, we have this:
cnt_array: | 4 | 2 | 0 |
array: | &fgraph_ops_1 | &fgraph_ops_2 | &fgraph_ops_stub | ...And do:
for (i = 0; i < array_entries; i++) {
if (array[i]->entryfunc(...)) {
idx = cnt_array[i] << 8 | i;
push idx onto ret_stack;
}
}Then on return we have:
idx = pop ret_stack; if (idx >> 8 == cnt_array[idx & 0xff]) array[idx & 0xff]->retfunc(...);
It wouldn't call fgraph_ops_3 because we would change the cnt_array when we remove fgraph_ops_2 and the return would not match, as cnt_array[1] would then be "3".
Quoted 20 lines
Quoted 9 lines
Quoted 8 lines
By the way, are there any way to hold a private data on each
ret_stackentry? Since kretprobe supports "entry data" passed fromentry_handlerto return handler, we have to store the data or data-instance on theret_stack.This feature is used by systemtap to save the function entry data, like function parameters etc., so that return handler analyzes the parameters with return value.
Quoted 10 lines
Yes, I remember you telling me about this at plumbers, and while I was writing this code I had that in mind. It wouldn't be too hard to implement, I just left it off for now. I also left it off because I have some questions about what exactly is needed. What size do you require to be stored. Especially if we want to keep the shadow stack smaller. I was going to find a way to implement some of the data that is already stored via the
ret_stackwith this instead, and make theret_stackentry smaller. Should we allow just sizeof(long)*3? or just let user choose any size and if they run out of stack, then too bad. We just wont let it crash.
Quoted 3 lines
I need only sizeof(unsigned long). If the kretprobe user requires more, it will be fall back to current method -- get an "instance" and store its address to the entry :-)
Awesome, then this shouldn't be too hard to implement.
-- Steve
Masami Hiramatsu
maintainerQuoted 9 lines · On Thu, 29 Nov 2018 11:46:52 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 6 lines · On Thu, 29 Nov 2018 23:29:27 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 5 lines
One way to solve this is to also have a counter array that gets updated every time the index array gets updated. And save the counter to the shadow stack index as well. This way, we only call the return if the counter on the stack matches what's in the counter on the counter array for the index.
Quoted 9 lines
Quoted 2 lines
Hmm, but we already know the current stack "header" entry when calling handlers, don't we? I thought we just calcurate out from
curr_ret_stack.Basically we have this:
array: |
&fgraph_ops_1|&fgraph_ops_2|&fgraph_ops_stub| ...On entry of function we do:
push header(including original ret_addr) onto ret_stack
Quoted 11 lines
for (i = 0; i < array_entries; i++) { if (array[i]->entryfunc(...)) { push i onto ret_stack; } }On the return side, we do:
idx = pop ret_stack; array[idx]->retfunc(...);
So at this point we have the header on ret_stack, don't we? :)
Anyway, I think we may provide an API for unwinder to find correct original return address form ret_stack. That is OK for me.
Quoted 61 lines
We only call the retfunc of a
fgraph_opsif it returned non-zero from itsentryfunc(). The return can happen a long time from now (which is why I don't save the&fgraph_opson theret_stack, because then we would never be able to free it).In the mean time, lets say we unregistered (and freed)
fgraph_ops_2and then addedfgraph_ops_3, so the array looks like:array: |
&fgraph_ops_1|&fgraph_ops_3|&fgraph_ops_stub| ...Then a function that was called when
fgraph_ops_2was on the stack returns, it will call array[1]->retfunc() which now belongs tofgraph_ops_3and notfgraph_ops_2.But if we add a counter array that gets updated when new ops are added to the array, we have this:
cnt_array: | 4 | 2 | 0 | array: | &fgraph_ops_1 | &fgraph_ops_2 | &fgraph_ops_stub | ...And do:
for (i = 0; i < array_entries; i++) { if (array[i]->entryfunc(...)) { idx = cnt_array[i] << 8 | i; push idx onto ret_stack; } }Then on return we have:
idx = pop ret_stack; if (idx >> 8 == cnt_array[idx & 0xff]) array[idx & 0xff]->retfunc(...);It wouldn't call
fgraph_ops_3because we would change thecnt_arraywhen we removefgraph_ops_2and the return would not match, as cnt_array[1] would then be "3".Quoted 20 lines
Quoted 19 lines
Quoted 8 lines
By the way, are there any way to hold a private data on each
ret_stackentry? Since kretprobe supports "entry data" passed fromentry_handlerto return handler, we have to store the data or data-instance on theret_stack.This feature is used by systemtap to save the function entry data, like function parameters etc., so that return handler analyzes the parameters with return value.
Yes, I remember you telling me about this at plumbers, and while I was writing this code I had that in mind. It wouldn't be too hard to implement, I just left it off for now. I also left it off because I have some questions about what exactly is needed. What size do you require to be stored. Especially if we want to keep the shadow stack smaller. I was going to find a way to implement some of the data that is already stored via the
ret_stackwith this instead, and make theret_stackentry smaller. Should we allow just sizeof(long)*3? or just let user choose any size and if they run out of stack, then too bad. We just wont let it crash.
Quoted 5 lines
Quoted 3 lines
I need only sizeof(unsigned long). If the kretprobe user requires more, it will be fall back to current method -- get an "instance" and store its address to the entry :-)
Awesome, then this shouldn't be too hard to implement.
Oops, anyway I noticed that I must store a value on each area so that we can identify which kretprobe is using that if there are several kretprobes on same function. So, kretprobe implementation will be something like below.
kretprobe_retfunc(trace, regs)
{
kp = get_kprobe(trace->func);
if (private == to_kretprobe(kp)) // this is directly mapped to current kprobe
goto found_kretprobe;
if (!list_empty(&kp->list)) { // we need to find from multiple kretprobes
list_for_each_entry(kp, &kp->list, list)
if (private == kp)
goto found_kretprobe;
}
// Or this must be an instance
struct kretprobe_instance *ri = trace->private;
rp = ri->rp;
if (valid_kretprobe(rp))
rp->handler(ri, regs);
kretprobe_recycle_instance(ri);
goto out;found_kretprobe:
struct kretprobe_instance rii = {.rp = to_kretprobe(kp),
.ret_addr=trace->ret, .task = current}
rp->handler(&rii, regs);out:
return 0; }
I think we talked about pt_regs, which is redundant for return probe, so it should be just a return value. (but how we pass it? trace->retval?)
That is OK for ftrace (but the transition needs more code).
And I would like to ask ebpf and systemtap people that is OK since it will change the kernel ABI.
Thank you,
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerQuoted 15 lines · On Fri, 30 Nov 2018 11:26:58 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 12 lines · On Thu, 29 Nov 2018 11:46:52 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 8 lines · On Thu, 29 Nov 2018 23:29:27 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 5 lines
One way to solve this is to also have a counter array that gets updated every time the index array gets updated. And save the counter to the shadow stack index as well. This way, we only call the return if the counter on the stack matches what's in the counter on the counter array for the index.
Hmm, but we already know the current stack "header" entry when calling handlers, don't we? I thought we just calcurate out from
curr_ret_stack.
Quoted 7 lines
Quoted 6 lines
Basically we have this:
array: |
&fgraph_ops_1|&fgraph_ops_2|&fgraph_ops_stub| ...On entry of function we do:
push
header(including original ret_addr)ontoret_stack
We can't put the ret_addr of the callback on the stack. What if that ret_addr is a module, and it gets unloaded? We must not call it.
Quoted 9 lines
Quoted 9 lines
for (i = 0; i < array_entries; i++) { if (array[i]->entryfunc(...)) { push i onto ret_stack; } }On the return side, we do:
Quoted 8 lines
Quoted 3 lines
idx = pop ret_stack; array[idx]->retfunc(...);So at this point we have the header on
ret_stack, don't we? :)Anyway, I think we may provide an API for unwinder to find correct original return address form
ret_stack. That is OK for me.
Yes. In fact, I have something that worked for that. I'll have to test it some more.
Quoted 36 lines
Quoted 5 lines
Quoted 3 lines
I need only sizeof(unsigned long). If the kretprobe user requires more, it will be fall back to current method -- get an "instance" and store its address to the entry :-)
Awesome, then this shouldn't be too hard to implement.
Oops, anyway I noticed that I must store a value on each area so that we can identify which kretprobe is using that if there are several kretprobes on same function. So, kretprobe implementation will be something like below.
kretprobe_retfunc(trace, regs){ kp = get_kprobe(trace->func); if (private == to_kretprobe(kp)) // this is directly mapped to current kprobe goto found_kretprobe; if (!list_empty(&kp->list)) { // we need to find from multiple kretprobes list_for_each_entry(kp, &kp->list, list) if (private == kp) goto found_kretprobe; } // Or this must be an instance struct kretprobe_instance *ri = trace->private; rp = ri->rp; if (valid_kretprobe(rp)) rp->handler(ri, regs); kretprobe_recycle_instance(ri); goto out;
found_kretprobe:struct kretprobe_instance rii = {.rp = to_kretprobe(kp), .ret_addr=trace->ret, .task = current} rp->handler(&rii, regs);
Quoted 6 lines
out:
return 0; }I think we talked about
pt_regs, which is redundant for return probe, so it should be just a return value. (but how we pass it?trace->retval?)
Yeah, we can add that.
Quoted 3 lines
That is OK for ftrace (but the transition needs more code).
And I would like to ask ebpf and systemtap people that is OK since it will change the kernel ABI.
I agree.
-- Steve
Masami Hiramatsu
maintainerQuoted 20 lines · On Thu, 29 Nov 2018 22:24:35 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 17 lines · On Fri, 30 Nov 2018 11:26:58 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 14 lines · On Thu, 29 Nov 2018 11:46:52 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 8 lines · On Thu, 29 Nov 2018 23:29:27 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 5 lines
One way to solve this is to also have a counter array that gets updated every time the index array gets updated. And save the counter to the shadow stack index as well. This way, we only call the return if the counter on the stack matches what's in the counter on the counter array for the index.
Hmm, but we already know the current stack "header" entry when calling handlers, don't we? I thought we just calcurate out from
curr_ret_stack.Basically we have this:
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
array: |
&fgraph_ops_1|&fgraph_ops_2|&fgraph_ops_stub| ...On entry of function we do:
push
header(including original ret_addr)ontoret_stackWe can't put the
ret_addrof the callback on the stack. What if thatret_addris a module, and it gets unloaded? We must not call it.
But in that case, how can we recover the original addr on the kernel (real) stack? I don't call the entry, but kretprobe handler will need the info to record as a caller-address.
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines
for (i = 0; i < array_entries; i++) { if (array[i]->entryfunc(...)) { push i onto ret_stack; } }On the return side, we do:
idx = pop ret_stack; array[idx]->retfunc(...);
Quoted 7 lines
Quoted 4 lines
So at this point we have the header on
ret_stack, don't we? :)Anyway, I think we may provide an API for unwinder to find correct original return address form
ret_stack. That is OK for me.Yes. In fact, I have something that worked for that. I'll have to test it some more.
Great! I think it will be enough for kretprobe.
Quoted 36 lines
Quoted 36 lines
Quoted 5 lines
Quoted 3 lines
I need only sizeof(unsigned long). If the kretprobe user requires more, it will be fall back to current method -- get an "instance" and store its address to the entry :-)
Awesome, then this shouldn't be too hard to implement.
Oops, anyway I noticed that I must store a value on each area so that we can identify which kretprobe is using that if there are several kretprobes on same function. So, kretprobe implementation will be something like below.
kretprobe_retfunc(trace, regs){ kp = get_kprobe(trace->func); if (private == to_kretprobe(kp)) // this is directly mapped to current kprobe goto found_kretprobe; if (!list_empty(&kp->list)) { // we need to find from multiple kretprobes list_for_each_entry(kp, &kp->list, list) if (private == kp) goto found_kretprobe; } // Or this must be an instance struct kretprobe_instance *ri = trace->private; rp = ri->rp; if (valid_kretprobe(rp)) rp->handler(ri, regs); kretprobe_recycle_instance(ri); goto out;
found_kretprobe:struct kretprobe_instance rii = {.rp = to_kretprobe(kp), .ret_addr=trace->ret, .task = current} rp->handler(&rii, regs);
Quoted 8 lines
Quoted 6 lines
out:
return 0; }I think we talked about
pt_regs, which is redundant for return probe, so it should be just a return value. (but how we pass it?trace->retval?)Yeah, we can add that.
OK, then I will start with making a fake pt_regs on stack and call handler, which will be something like,
struct pt_regs regs = {};
regs_set_return_value(®s, trace->retval);
rp->handler(ri, ®s);Thank you,
Quoted 7 lines
Quoted 3 lines
That is OK for ftrace (but the transition needs more code).
And I would like to ask ebpf and systemtap people that is OK since it will change the kernel ABI.I agree.
-- Steve
Signature · 1 line
Masami Hiramatsu <[email protected]>