-Cforce-frame-pointers flag seems to be broken, how to enable frame pointers in release?

I need to enable frame pointers for --release build, but so far only debug mode works fine.

I know of some merges in rustc enabling frame pointers by default but I don't know the current status.
Does it even suppose to work? Did I hit a bug or something?
Cause if -Cforce-frame-pointers flag works only for debug builds its kinda useless.
And I need to enable frame pointers anyway.

Tried to do as described here

-Cforce-frame-pointers should work just fine. We also enable it for the standard library nowadays.

then why perf (set to fp mode) and bcc (has only fp) profilers don't want to work?
I also have no problems with C/GCC and C++/Clang binaries.

don't want to work = broken stack traces (with missing function names, etc)

At least a simple example like fn main() { loop { foo() } } #[inline(never)] fn foo() { print!(""); } when compiled with rustc - -Copt-level=3 -Cforce-frame-pointers correctly handles backtraces in perf with --call-graph=fp for me. Did you perhaps enable stripping in release mode?

yeah, perf works as expected, even on other examples.
but not bcc (which Is the main reason I want frame pointers).

this is what I expect (works in debug mode)

and this what I get

the sequence a -> b -> c is missing on the 2nd picture!
I learned about missing fp in compiled languages problem from BPF Performance Tools book.
If its not frame pointers, then what?

Does it have something to do with strange function names?

Are you missing (/expecting no) inlining?

No, I added no-inlines everywhere, perf can see them correctly.
Also tried adding no-mangle, didn't change anything.

Another detail: even opt-level=1 breaks everything in bcc case.

Assuming that a, b and c do nothing other than call another function, this is likely caused by tail call optimization. Basically whenever possible LLVM will replace the last call in a function body with a direct jump to the callee. This means that the stack frame for the function is gone unrecoverably and neither profilers nor debuggers can figure out that the stack frame should have existed. Even when compiling with frame pointers and full debuginfo.

2 Likes

a, b, c look like this

    #[inline(never)]
    pub fn print_this_line(d: impl Display) {
        println!("{}", d);
    }

    #[inline(never)]
    pub fn a() {
        print_this_line("a");
        b();
    }

    #[inline(never)]
    fn b() {
        print_this_line("b");
        c();
        thread::sleep(Duration::from_millis(3));
    }

    #[inline(never)]
    fn c() {
        print_this_line("c");
        thread::sleep(Duration::from_millis(17));
    }

I can see every one of them in the perf report.
Compiled in release mode with RUSTFLAGS='-Cforce-frame-pointers'

Larger functions have same issues

Does your release build include debuginfo? Perhaps perf does recover some information from that which bcc doesn't.

Tried with and without debug info, same results