Skip to content

-C force-frame-pointers=yes not respected by -Z build-std or opt-level = "z" #136198

Open
@rslawson

Description

@rslawson

So I've got a project I'm trying to debug, but I'm running into something of an issue. The setup is that I have RISC-V CPUs (riscv32imc-unknown-none-elf) debuggable over JTAG using OpenOCD + GDB. I would like to be able to get a backtrace out of GDB in the case that a program panics, but previously I was never able to guarantee that such a trace would be there since the stack would collapse as soon as execution entered into core::panicking::panic or the like. The way I was attempting to get backtraces from GDB was by doing something to the effect of

file target/riscv32imc-unknown-none-elf/release/crate
set remotetimeout unlimited
break crate::panic_handler

target extended-remote :3333
load

define hook-stop
printf "!!!!! hit panic, execution has stopped !!!!!\n"
backtrace
disconnect
quit 1
end

continue

I figured that if I:

  • Added panic = "abort" to the release profile inCargo.toml
  • Switched to nightly
  • Added the following to .cargo/config.toml:
[build]
# ...
# added:
rustflags = [
    # ...
    "-C", "force-frame-pointers=yes",
]
# ...

[unstable]
build-std = ["core", "panic_abort"]

Then in theory, I should be guaranteed to get backtraces. So I wrote a simple program:

#![no_std]
#![no_main]

// The particular impl here doesn't matter.
use some_hal_crate::uart::Uart;
use riscv_rt::entry;

const UART_ADDR: *const () = (0b11 << 30) as *const ();

// And neither does the implementation here - I'm just using it for some extra debugging output.
#[panic_handler]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
    let mut uart = Uart::new(UART_ADDR);
    writeln!(uart, "{info:?}").unwrap();
}

#[entry]
fn main() -> ! {
    skooks();
    loop {}
}

misadventures! {
    am i glad hes frozen in_ there and that were out here and_ that_ hes_ the
    sheriff and__ that__ were_ frozen_ out_ here_ and___ that___ were__ in__
    there__ and____ i_ just remembered were___ out__ here__ what i__ want to know
    is wheres the_ caveman
}

macro_rules! misadventures {
    (@rev [] [$($rev:ident)*]) => {
        misadventures! {
            @defs [skooks $($rev)*]
        }
    };
    (@rev [$first:ident $($rest:ident)*] [$($rev:ident)*]) => {
        misadventures! {
            @rev [$($rest)*] [$first $($rev)*]
        }
    };
    (@defs [$last:ident]) => {
        #[inline(never)]
        fn $last() {
            panic!();
        }
    };
    (@defs [$n0:ident $n1:ident $($rest:ident)*]) => {
        #[inline(never)]
        fn $n0() {
            $n1();
        }

        misadventures! {
            @defs [$n1 $($rest)*]
        }
    };
    ($($words:ident)+) => {
        misadventures! {
            @rev [$($words)+] []
        }
    }
}

pub(crate) use misadventures;

However, I have two problem cases:

  1. In debug builds, the stack trace disappears as soon as execution enters the panicking code (crate::am::panic_cold_explicit I believe). Until then, I get a full trace (very nice!).
  2. In release builds (with debug = true, split-debuginfo = "unpacked", and opt-level = "z"), the stack trace is never deeper than #0 and #1 in GDB. It reports Backtrace stopped: frame did not save the PC.

panic = "abort" was used for all of my testing.

Meta

This was done on nightly-2024-11-18.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-backtraceArea: BacktracesC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions