Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix libunwind backtraces & Make defaulttraceinfo more generic. #4639

Merged
merged 11 commits into from May 17, 2024

Conversation

JohanEngelen
Copy link
Member

@JohanEngelen JohanEngelen commented May 3, 2024

Libunwind-based druntime implementation for backtraces results in wrong line numbers (very confusing backtrace).

  1. Make DefaultTraceInfo smart enough to not strictly require execinfo.
  2. Stop using libunwind for musl. Fix libunwind implementation

…acktrace()`).

When execinfo is not supported, it falls back to manual unwinding, which is the same fallback as when `backtrace()` is not successful.
…nd thus confusing!) line numbers in the backtrace. For now, do not use libunwind for Musl.

This does mean that musl will use the fallback path in DefaultTraceInfo which uses manual unwinding, i.e. requiring `-frame-pointer=all`.
@JohanEngelen
Copy link
Member Author

JohanEngelen commented May 3, 2024

@Geod24 The libunwind implementation needs a rework. The testcase that shows broken behavior clearly is dmd/runnable/test17559.d.
On line 70, frame.address += pip.start_ip; is where the backtrace address for symbol:line resolution is stored. But frame.address is zero there, so it will be loaded with the function's entry address. Not so useful. Instead we should use something like unw_get_reg(&cursor, UNW_REG_IP, &ip); to get the address of the return address (after the function call instruction).

Edit: I now have a proper fix for libunwind aswell. Will need to iron out an issue with importC and @nogc nothrow.

@JohanEngelen
Copy link
Member Author

if it works here for LDC CI, I'll submit it upstream.

@JohanEngelen
Copy link
Member Author

I first had it working with importC, but the LLVM header of libunwind is pretty small so let's use the existing hard-coded binaries (easy to check that they are correct, and the additions needed are arch independent).

@JohanEngelen JohanEngelen changed the title Do not use libunwind for musl, use defaulttraceinfo Fix libunwind backtraces & Make defaulttraceinfo more generic. May 3, 2024
@kinke
Copy link
Member

kinke commented May 4, 2024

Nice that you're hunting down the musl issues! 👍 - How's it looking, many failures still remaining or close to the finish line? Just wrt. v1.38.0 final that I'd like to release this weekend, unless you are close and require a bit more time.

@JohanEngelen
Copy link
Member Author

Let's not wait for the musl work (yesterday I ran stdlib tests for the first time, many succeed, but e.g. fiber tests hung and I have not looked into that at all yet). I'm hoping to reach the endpoint with one Github Action that builds using an alpine container (quick to set up in linux), such that we have a fully statically linked musl release.

@Geod24
Copy link
Contributor

Geod24 commented May 4, 2024

Indeed, thanks for doing this! I haven't personally needed Musl for a few years so never got around to fixing the more tedious issues, but I do remember backtraces not being working fully...

@JohanEngelen
Copy link
Member Author

PR is fully finished. Feel free to squash and merge, or postpone until after 1.38 if deemed too risky.

@JohanEngelen
Copy link
Member Author

PR is fully finished. Feel free to squash and merge, or postpone until after 1.38 if deemed too risky.

there is still something wrong with backtraces. One bug I know (need to adjust backtrace address to point to call instruction instead of right after call instruction), but then there is still something buggy giving slightly incorrect backtrace on throwing an exception.

@JohanEngelen
Copy link
Member Author

Nice that you're hunting down the musl issues! 👍 - How's it looking, many failures still remaining or close to the finish line? Just wrt. v1.38.0 final that I'd like to release this weekend, unless you are close and require a bit more time.

To see current status:
https://github.com/ldc-developers/ldc/actions/runs/8976987555/job/24654919738

@JohanEngelen
Copy link
Member Author

PR is fully finished. Feel free to squash and merge, or postpone until after 1.38 if deemed too risky.

there is still something wrong with backtraces. One bug I know (need to adjust backtrace address to point to call instruction instead of right after call instruction), but then there is still something buggy giving slightly incorrect backtrace on throwing an exception.

This is fixed now (needed to adjust IP to point to the call instruction, rather than after the call).

@JohanEngelen JohanEngelen enabled auto-merge (squash) May 17, 2024 14:49
@JohanEngelen JohanEngelen merged commit 93afbfa into ldc-developers:master May 17, 2024
23 checks passed
@JohanEngelen JohanEngelen deleted the musl_backtrace branch May 17, 2024 22:54
@JohanEngelen
Copy link
Member Author

It turns out we can use this code fragment for libunwind aswell:

version (LDC) version (Darwin)
{
nothrow:
extern (C)
{
enum _URC_NO_REASON = 0;
enum _URC_END_OF_STACK = 5;
alias _Unwind_Context_Ptr = void*;
alias _Unwind_Trace_Fn = int function(_Unwind_Context_Ptr, void*);
int _Unwind_Backtrace(_Unwind_Trace_Fn, void*);
ptrdiff_t _Unwind_GetIP(_Unwind_Context_Ptr context);
}
// Use our own backtrce() based on _Unwind_Backtrace(), as the former (from
// execinfo) doesn't seem to handle missing frame pointers too well.
private int backtrace(void** buffer, int maxSize)
{
if (maxSize < 0) return 0;
struct State
{
void** buffer;
int maxSize;
int entriesWritten = 0;
}
static extern(C) int handler(_Unwind_Context_Ptr context, void* statePtr)
{
auto state = cast(State*)statePtr;
if (state.entriesWritten >= state.maxSize) return _URC_END_OF_STACK;
auto instructionPtr = _Unwind_GetIP(context);
if (!instructionPtr) return _URC_END_OF_STACK;
state.buffer[state.entriesWritten] = cast(void*)instructionPtr;
++state.entriesWritten;
return _URC_NO_REASON;
}
State state;
state.buffer = buffer;
state.maxSize = maxSize;
_Unwind_Backtrace(&handler, &state);
return state.entriesWritten;
}
}

So we can completely remove backtrace/handler.d. To be submitted in new PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants