On Tue, 22 Mar 2022 15:35:54 +0100 Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote: > On Tue, Mar 22, 2022 at 09:12:42AM -0400, Steven Rostedt wrote: > > > > Suppose: > > > > > > notrace func_B() > > > { > > > ... > > > } > > > > > > func_A() > > > { > > > ... > > > return func_B(); > > > } > > > > > > then inhibiting tail calls would end up looking like: > > > > If we inhibit tail calls, then we do not need to make func_B notrace. > > Dude, you're arguing in circles :-( the notrace was a given. > > > > func_A: > > > call __fentry__ > > > ... > > > call func_B > > > call __fexit__ > > > ret > > > > > > Then A is fully traced, B is invisible, as per spec. What is the > > > problem? > > > > The above is fine, but then func_B is not a tail call and can also be > > traced. > > Again, B is notrace as a given. This was all about how to deal with > notrace functions. > > I suggested inhibiting tail-call to notrace, you said no. You now seem to > agree that solves it. > > > > The problem you initially had, of doing a tail-call into a notrace, was > > > that the __fexit__ call went missing, because notrace will obviously not > > > have that. But that's avoided by inhibiting all tail-calls between > > > notrace and !notrace functions (note that notrace must also not > > > tail-call !notrace). > > > > I'm confused by the above. Why can't a notrace tail call a !notrace? > > If we tail call to a > > > > func_B: > > call __fentry__ > > ... > > call __fexit__ > > ret > > > > then the fentry and fexit show a perfectly valid trace of func_B. > > Bah; I thought I had a case this morning, but now I can't seem to recall > :/ > > > > Your worry seems to stem about loosing visiblilty of !notrace functions, > > > but AFAICT that doesn't happen. > > > > My worry is: > > > > func_A: > > call __fentry__ > > ... > > jmp func_B > > > > Where do we do the call __fexit__ ? > > In B (or wherever if B again does a tail-call). > > > That was the original concern, and I think the proposed solutions have > > convoluted our thoughts about what we are trying to fix. So let's go back > > to the beginning, and see how to deal with it. > > > > That is, we have: > > > > func_C: > > call __fenty__ > > ... > > call func_A: > > ... > > call func_B: > > ... > > call __fexit__ > > ret > > > > func_A: > > call __fentry__ > > ... > call __ftail__ > > jmp func_B > > > > func_B: > > call __fentry__ > > ... > > call __fexit__ > > ret > > > > Where the above is C calling A and B as normal functions, A calling B as a > > tail call and B just being a normal function called by both A and C (and > > many other functions). > > We need the __ftail__ thing to mark the trace-stack entry of func_A as > complete, then any future __fexit__ will be able to pop all completed > entries. > > In recap: > > __fentry__ -- push on trace-stack > __ftail__ -- mark top-most entry complete > __fexit__ -- mark top-most entry complete; > pop all completed entries > > inhibit tail-calls to notrace. > > > And note, I do not want to limit function tracing (which does not rely on > > __fexit__) just because we can't figure out how to handle __fexit__. > > I'm not following. Regular function tracing needs none of this. > > It's function graph tracing, kretprobes and whatever else this rethook > stuff is about that needs this because return trampolines will stop > working somewhere in the not too distant future. I see the __fexit__ is needed, but why __ftail__ is needed? I guess because func_B is notrace, in that case the __fexit__ will not be in the func_B. Am I correct? Thank you, -- Masami Hiramatsu <mhiramat@xxxxxxxxxx>