jeremy-rifkin / cpptrace

Simple, portable, and self-contained stacktrace library for C++11 and newer
MIT License
642 stars 67 forks source link

Stack is empty with `-fno-exceptions` on m2 macbook #161

Closed Ghost-LZW closed 3 days ago

Ghost-LZW commented 2 weeks ago

We can build this library without the -no-exceptions flag and then link it with a no-exceptions library. However, I found that without exception support, the stack will be empty. Here is an simple example.

cmake_minimum_required(VERSION 3.24...3.26)

project(test)
set(CMAKE_CXX_STANDARD 20)

include(FetchContent)
FetchContent_Declare(
  cpptrace
  GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
  GIT_TAG        v0.7.0 # <HASH or TAG>
)
FetchContent_MakeAvailable(cpptrace)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")

add_executable(my_target test.cc)

target_link_libraries(my_target cpptrace::cpptrace)

# Create a .dSYM file on macOS
if(APPLE)
  add_custom_command(
    TARGET my_target
    POST_BUILD
    COMMAND dsymutil $<TARGET_FILE:my_target>
  )
endif()
#include "cpptrace/cpptrace.hpp"

void test() {
  cpptrace::generate_trace().print();
}

int main() {
  test();
}

/*
Stack trace (most recent call first):
<empty trace>
*/

I test it on Apple M2 Pro macbook.

Ghost-LZW commented 2 weeks ago

I found it can work on linux.

Stack trace (most recent call first):
#0 0x00000000004036fe in test() at cmake_test/test.cc:7:27
#1 0x0000000000403722 in main at cmake_test/test.cc:11:7
#2 0x00007f4f0f87909a in __libc_start_main at /libc-start.c:308:16
#3 0x0000000000403629 at /cmake_test/build/my_target
jeremy-rifkin commented 2 weeks ago

Thank you for opening this and providing suchc a clear minimal reproducible example. I will look into this tomorrow.

jeremy-rifkin commented 2 weeks ago

Alright, I have reproduced the behavior you describe. I think what's happening here is that _Unwind_Backtrace relies on unwinding tables on arm and these aren't generated under -fno-exceptions. I've seen this before in #134. A workaround is to use execinfo's unwinding, -DCPPTRACE_UNWIND_WITH_EXECINFO=On, which doesn't rely on these. I expect libunwind would also work, however, I haven't tested that.

I'll look into how to and whether to provide a better library default here to prevent issues in the future.

Ghost-LZW commented 2 weeks ago

libunwind indeed work, I test it with this patch https://github.com/jeremy-rifkin/cpptrace/pull/162

# cmake -S . -B build_libunwind -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_UNWIND_WITH_LIBUNWIND=ON
Stack trace (most recent call first):

0 0x00000001022f31bb in test() at /cmake_test/test.cc:7:3

1 0x00000001022f31e3 in main at /cmake_test/test.cc:11:3

2 0x000000018da390df at /usr/lib/dyld

I found that I might not have built it correctly, which means the output is wrong. The stack is generated but does not properly resolve symbols, similar to jeremy-rifkin's result.

jeremy-rifkin commented 2 weeks ago

Thanks so much for taking the time to figure that out! I gave your patch a try with the repro setup using PATCH_COMMAND and it built using libunwind however unfortunately I still got an empty trace. If it worked for you and not for me I'd like to understand why. It might be a configuration issue on my end.

Ghost-LZW commented 2 weeks ago

Sorry for the synchronization issue. When I use GCC on macOS, the stack is generated correctly (even with unwind). However, with Apple Clang or Clang, the stack remains empty.

jeremy-rifkin commented 2 weeks ago

Thanks for clarifying, interesting that it doesn't work on clang

jeremy-rifkin commented 3 days ago

Tested a number of configurations:

I didn't test -stdlib=libstdc++ behavior. I will proceed with setting execinfo as the default for clang on apple.