Re: ftrace not showing the process names for all processes on syscall events

From: Steven Rostedt
Date: Mon Mar 30 2020 - 14:07:41 EST


On Mon, 30 Mar 2020 15:34:08 +0000
David Laight <David.Laight@xxxxxxxxxx> 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