Messages in this thread | | | Date | Mon, 30 Mar 2020 14:07:36 -0400 | From | Steven Rostedt <> | Subject | Re: ftrace not showing the process names for all processes on syscall events |
| |
On Mon, 30 Mar 2020 15:34:08 +0000 David Laight <David.Laight@ACULAB.COM> wrote:
> Oh, does the 'function_graph' code ignore tail calls?
Yes and no ;-) It works by dumb luck. As it was a year after function graph tracing was live (some time in 2010 I believe) that someone brought up tail calls, and I had to take a look at how it never crashed, and was surprised that it "just worked". Here's a summary:
The function graph tracer works via the following:
start of function being traced:
call fgraph trampoline
Replace return code on stack with jump to return trampoline save original return address in shadow stack return back to start of function
end of function:
pop return address jump to return address which is now a jump to the return trampoline:
Return trampoline: trace return of function pop the shadow stack to get the original return address jump to original return address
Now let's take a look at a tail function call:
first_func() { some_stuff; tail_func(); return; }
tail_func() { some_other_stuff; return; }
The above would look like in assembly something like this:
first_func:
1: call fgaph_tramp 2: asm some_stuff 3: jmp tail_func
tail_func:
4: call fgraph_trapm 5: asm some_other_stuff 6: pop 7: ret
At 1 above, we call fgraph_tramp which will replace the return address on the stack (what called first_func) with the address of the fgraph return trampoline. And store the original address onto the shadow stack.
The stack looks like this:
main stack: ----------- fgraph return trampoline
shadow stack: ------------- original return address
At 3, we just jump directly to tail_func, without doing anything to the stack.
At 4, fgraph_tramp is called for tail_func this time, and it will replace its return address with a call to the fgraph return trampoline. HERE IS THE TRICK! The return address that was replaced, was actually the address of the fgraph return trampoline and not the original return address. And this is now stored on the shadow stack. Thus we have this:
main stack: ----------- fgraph return trampoline
shadow stack: ------------- original return address fgraph return trampoline
At 6, the fgraph return trampoline is popped off the stack, and called.
The fgraph return trampoline, will trace the end of the function, pop off the shadow stack, and jump to that. It just happen that it pops off the address of the fgraph return trampoline and jumps to itself again.
The second call to fgraph return trampline will trace the end of first_func, pop the shadow stack, which this time is the original return address, and then jump to that.
Thus, it "just works" and we can all stay happy ;-)
-- Steve
| |