Stream: general

Topic: missing stackframes in backtrace


zeroexcuses (May 11 2020 at 14:24, on Zulip):

I'm having the following problem: in my backtrace dump (from backtrace-rs crate), certain frames that I expect to be present are not. Is it possible that opt-level 3 is optimizing certain functions away ?

Laurențiu Nicola (May 11 2020 at 14:26, on Zulip):

What if you set [profile.release] debug = 2 in your Cargo.toml?

zeroexcuses (May 11 2020 at 14:27, on Zulip):

Why profile.release? I'm currently trying:

[profile.dev]
lto = false
opt-level = 2

[profile.test]
lto = false
opt-level = 2

[profile.dev.overrides."*"]
lto = false
opt-level = 2
zeroexcuses (May 11 2020 at 14:28, on Zulip):

(btw, on opt-level = 3, even the CodeLLDB debugger doesn't get the stack frame either, which makes me suspect it being inlined / optimized away.)

zeroexcuses (May 11 2020 at 14:28, on Zulip):

(ps2: opt-level = 3 + #[inline(never)] ==> stackframe still missing)

zeroexcuses (May 11 2020 at 14:29, on Zulip):

opt-level = 2 ==> missing some stackframe
opt-level = 0 ==> all stackframes
trying opt-level = 1 next

Laurențiu Nicola (May 11 2020 at 14:30, on Zulip):

Ah, never mind if you're not building with --release

zeroexcuses (May 11 2020 at 14:31, on Zulip):

It's running cargo test --package ....

Laurențiu Nicola (May 11 2020 at 14:31, on Zulip):

The test profile already has debug = 2

zeroexcuses (May 11 2020 at 14:32, on Zulip):

opt-level = 3, 2, 1 => missing some stackframe
opt-level = 0 => all stackframes

zeroexcuses (May 11 2020 at 14:32, on Zulip):

This is annoying, as I'm writing numerical code, and I want some optimization ; but I'm also doing tdd, so I want all my stackframes.

Mark Drobnak (May 11 2020 at 14:38, on Zulip):

Your tests rely on knowing stackframes?

zeroexcuses (May 11 2020 at 14:48, on Zulip):

Even

[profile.dev]
lto = false
opt-level = 2
debuginfo=2
debug-assertions=true
force-frame-pointers = true
force-unwind-tables = true

[profile.test]
lto = false
opt-level = 2
debuginfo=2
debug-assertions=true
force-frame-pointers = true
force-unwind-tables = true

[profile.dev.overrides."*"]
lto = false
opt-level = 2
debuginfo=2
debug-assertions=true
force-frame-pointers = true
force-unwind-tables = true

doesn't work

zeroexcuses (May 11 2020 at 14:49, on Zulip):

@Mark Drobnak : My code has lots of "todo!()"'s -- when a unit test fails, I'd like to be able to see the entire callstack. Currently some of the frames are being optimized away when opt-level > 0.

Mark Drobnak (May 11 2020 at 14:49, on Zulip):

Can't you just set opt-level = 0 for tests and leave everything else alone?

zeroexcuses (May 11 2020 at 14:50, on Zulip):

There alot of code that operates on tensors of f32's. The tests run really really slow at opt-level = 0.

Mark Drobnak (May 11 2020 at 14:51, on Zulip):

Unless there's a flag to avoid inlining, that sounds like your only option.

zeroexcuses (May 11 2020 at 14:52, on Zulip):

I've already tried #[inline(never)] and #[no_mangle].

Laurențiu Nicola (May 11 2020 at 14:53, on Zulip):

Optimizations generally reduce the quality of stack traces

zeroexcuses (May 11 2020 at 14:56, on Zulip):

I'm going to throw in the towel now. This was an interesting excuse to study rustc compiler flags. Thanks to everyone for patience / ideas / suggestions.

bjorn3 (May 11 2020 at 15:42, on Zulip):

Could the missing stack frames have tail calls? (a call just before returning from a function without any destructors running afterwards) In that case LLVM likely replaced the call with a jump, which means that there is no way to recover the original call frame.

Jake Goulding (May 11 2020 at 16:29, on Zulip):

JIC, you can do todo!("thing 1") instead of just todo!(). That text is printed out when triggered

zeroexcuses (May 11 2020 at 16:57, on Zulip):

@bjorn3 : I have no idea how you guessed this -- but it often is of the form:

panic!()
todo!()
return Err(...)

and the last statement/expr in a function (if not directly, then the last in that particular match branch)[

bjorn3 (May 11 2020 at 17:15, on Zulip):

@zeroexcuses Rust used to hide all libstd internal stack frames at the end of the backtrace. This unfortunately broke because LLVM decided to tail call from the function that indicated the start of the libstd internal stack frames (__rust_begin_short_backtrace) to the function it called. This meant that __rust_begin_short_backtrace wouldn't be part of the backtrace anymore. I remembered this problem, and thought that your problem may have been similar.

Last update: May 29 2020 at 18:05UTC