diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc8da81..47d2e2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,3 +136,60 @@ jobs: - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} + + + unittest-linux: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + compiler: [g++-10, clang++-14] + shared: [OFF, ON] + build_type: [Debug, RelWithDebInfo] + has_dl_find_object: [OFF, ON] + steps: + - uses: actions/checkout@v4 + - name: dependencies + run: | + sudo apt install gcc-10 g++-10 libgcc-10-dev ninja-build + - name: build and test + run: | + mkdir build + cd build + cmake .. \ + -GNinja \ + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ + -DCMAKE_C_COMPILER=${{matrix.compiler == 'g++-10' && 'gcc-10' || 'clang-14'}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + -DBUILD_SHARED_LIBS=${{matrix.shared}} \ + -DHAS_DL_FIND_OBJECT=${{matrix.has_dl_find_object}} \ + -DCPPTRACE_WERROR_BUILD=On \ + -DCPPTRACE_BUILD_TESTING=On + ninja + ./unittest + bash -c "exec -a u ./unittest" + unittest-windows: + runs-on: windows-2022 + strategy: + fail-fast: false + matrix: + compiler: [cl, clang++] + shared: [OFF] # TODO: Re-enable shared + build_type: [Debug, RelWithDebInfo] + steps: + - uses: actions/checkout@v4 + - name: Enable Developer Command Prompt + uses: ilammy/msvc-dev-cmd@v1.13.0 + - name: build and test + run: | + mkdir build + cd build + cmake .. ` + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} ` + -DCMAKE_C_COMPILER=${{matrix.compiler == 'clang++' && 'clang' || matrix.compiler}} ` + -DBUILD_SHARED_LIBS=${{matrix.shared}} ` + -DCPPTRACE_WERROR_BUILD=On ` + -DCPPTRACE_BUILD_TESTING=On + cmake --build . --config ${{matrix.build_type}} + ./${{matrix.build_type}}/unittest + # TODO: Macos, mingw diff --git a/ci/setup-prerequisites-mingw.ps1 b/ci/setup-prerequisites-mingw.ps1 index 6be6c31..8cbfb73 100644 --- a/ci/setup-prerequisites-mingw.ps1 +++ b/ci/setup-prerequisites-mingw.ps1 @@ -10,7 +10,9 @@ cd build cmake .. -DZSTD_BUILD_SHARED=On -DZSTD_BUILD_SHARED=Off -DZSTD_LEGACY_SUPPORT=Off -DZSTD_BUILD_PROGRAMS=Off -DZSTD_BUILD_CONTRIB=Off -DZSTD_BUILD_TESTS=Off -G"Unix Makefiles" make -j make install + cd ../../../.. + mkdir libdwarf cd libdwarf git init diff --git a/ci/setup-prerequisites.sh b/ci/setup-prerequisites.sh index 5cfdb77..107642a 100755 --- a/ci/setup-prerequisites.sh +++ b/ci/setup-prerequisites.sh @@ -1,4 +1,6 @@ #!/bin/bash +sudo apt install libgtest-dev + mkdir zstd cd zstd git init @@ -7,7 +9,9 @@ git fetch --depth 1 origin 63779c798237346c2b245c546c40b72a5a5913fe # 1.5.5 git checkout FETCH_HEAD make -j sudo make install + cd .. + mkdir libdwarf cd libdwarf git init diff --git a/ci/test-all-configs.py b/ci/test-all-configs.py index af5de80..70021f8 100644 --- a/ci/test-all-configs.py +++ b/ci/test-all-configs.py @@ -177,7 +177,7 @@ def build(matrix): f"-D{matrix['demangle']}=On", "-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h", "-DCPPTRACE_BUILD_TESTING=On", - "-DCPPTRACE_IS_GH_ACTIONS=On", + "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix['symbols'] == "CPPTRACE_GET_SYMBOLS_WITH_LIBDL": @@ -200,7 +200,7 @@ def build(matrix): f"-D{matrix['symbols']}=On", f"-D{matrix['demangle']}=On", "-DCPPTRACE_BUILD_TESTING=On", - "-DCPPTRACE_IS_GH_ACTIONS=On", + "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix["compiler"] == "g++": @@ -227,7 +227,7 @@ def build_full_or_auto(matrix): f"-DCPPTRACE_WERROR_BUILD=On", f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h", "-DCPPTRACE_BUILD_TESTING=On", - "-DCPPTRACE_IS_GH_ACTIONS=On", + "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix["config"] != "": @@ -247,7 +247,7 @@ def build_full_or_auto(matrix): f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_WERROR_BUILD=On", "-DCPPTRACE_BUILD_TESTING=On", - "-DCPPTRACE_IS_GH_ACTIONS=On", + "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix["config"] != "": diff --git a/cmake/OptionVariables.cmake b/cmake/OptionVariables.cmake index 6d18d02..716df4a 100644 --- a/cmake/OptionVariables.cmake +++ b/cmake/OptionVariables.cmake @@ -166,7 +166,7 @@ option(CPPTRACE_VCPKG "" OFF) option(CPPTRACE_SANITIZER_BUILD "" OFF) option(CPPTRACE_WERROR_BUILD "" OFF) option(CPPTRACE_POSITION_INDEPENDENT_CODE "" ON) -option(CPPTRACE_IS_GH_ACTIONS "" OFF) +option(CPPTRACE_SKIP_UNIT "" OFF) option(CPPTRACE_USE_EXTERNAL_GTEST "" OFF) mark_as_advanced( @@ -177,6 +177,6 @@ mark_as_advanced( CPPTRACE_WERROR_BUILD CPPTRACE_CONAN CPPTRACE_VCPKG - CPPTRACE_IS_GH_ACTIONS + CPPTRACE_SKIP_UNIT CPPTRACE_USE_EXTERNAL_GTEST ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e1cf272..1fc6c66 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -37,7 +37,7 @@ if(UNIX) endif() # primarily a workaround for github actions issue https://github.com/actions/runner-images/issues/8659 -if(NOT CPPTRACE_IS_GH_ACTIONS) +if(NOT CPPTRACE_SKIP_UNIT) if(CPPTRACE_USE_EXTERNAL_GTEST) find_package(GTest) else() @@ -60,6 +60,6 @@ if(NOT CPPTRACE_IS_GH_ACTIONS) ) target_compile_features(unittest PRIVATE cxx_std_20) target_link_libraries(unittest PRIVATE ${target_name} GTest::gtest_main GTest::gmock_main) - target_compile_options(unittest PRIVATE ${warning_options} -Wno-pedantic) + target_compile_options(unittest PRIVATE ${warning_options} $<$>:-Wno-pedantic -Wno-attributes>) add_test(NAME unittest COMMAND unittest) endif() diff --git a/test/unit/object_trace.cpp b/test/unit/object_trace.cpp index 65a907e..e96db11 100644 --- a/test/unit/object_trace.cpp +++ b/test/unit/object_trace.cpp @@ -49,16 +49,25 @@ TEST(ObjectTrace, BasicResolution) { } - -CPPTRACE_FORCE_NO_INLINE void object_resolve_3(std::vector& line_numbers) { +// TODO: dbghelp uses raw address, not object +#ifndef _MSC_VER +CPPTRACE_FORCE_NO_INLINE int object_resolve_3(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto dummy = cpptrace::generate_trace(); auto dummy_otrace = cpptrace::generate_object_trace(); cpptrace::object_trace otrace; - otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[0].object_address, dummy_otrace.frames[0].object_path}); - otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[1].object_address, dummy_otrace.frames[1].object_path}); - otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[2].object_address, dummy_otrace.frames[2].object_path}); - otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[3].object_address, dummy_otrace.frames[3].object_path}); + otrace.frames.push_back( + cpptrace::object_frame{0, dummy.frames[0].object_address, dummy_otrace.frames[0].object_path} + ); + otrace.frames.push_back( + cpptrace::object_frame{0, dummy.frames[1].object_address, dummy_otrace.frames[1].object_path} + ); + otrace.frames.push_back( + cpptrace::object_frame{0, dummy.frames[2].object_address, dummy_otrace.frames[2].object_path} + ); + otrace.frames.push_back( + cpptrace::object_frame{0, dummy.frames[3].object_address, dummy_otrace.frames[3].object_path} + ); auto trace = otrace.resolve(); int i = 0; EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("object_trace.cpp")); @@ -76,16 +85,17 @@ CPPTRACE_FORCE_NO_INLINE void object_resolve_3(std::vector& line_numbers) { EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("object_trace.cpp")); EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("ObjectTrace_Resolution_Test::TestBody")); + return 2; } -CPPTRACE_FORCE_NO_INLINE void object_resolve_2(std::vector& line_numbers) { +CPPTRACE_FORCE_NO_INLINE int object_resolve_2(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); - object_resolve_3(line_numbers); + return object_resolve_3(line_numbers) * 2; } -CPPTRACE_FORCE_NO_INLINE void object_resolve_1(std::vector& line_numbers) { +CPPTRACE_FORCE_NO_INLINE int object_resolve_1(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); - object_resolve_2(line_numbers); + return object_resolve_2(line_numbers) * 2; } TEST(ObjectTrace, Resolution) { @@ -93,3 +103,4 @@ TEST(ObjectTrace, Resolution) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); object_resolve_1(line_numbers); } +#endif diff --git a/test/unit/raw_trace.cpp b/test/unit/raw_trace.cpp index 0e83ceb..2ddc72c 100644 --- a/test/unit/raw_trace.cpp +++ b/test/unit/raw_trace.cpp @@ -16,7 +16,8 @@ using namespace std::literals; // This is fickle, however, it's the only way to do it really. It's reasonably reliable test in practice. -CPPTRACE_FORCE_NO_INLINE void raw_trace_basic() { +// NOTE: MSVC likes creating trampoline-like entries for non-static functions +CPPTRACE_FORCE_NO_INLINE static void raw_trace_basic() { auto raw_trace = cpptrace::generate_raw_trace(); // look for within 90 bytes of the start of the function EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_basic)); @@ -28,7 +29,14 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_basic_precise() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: - // look for within 30 bytes of the start of the function + // This is stupid, but without it gcc was optimizing both &&a and &&b to point to the start of the function's body + volatile auto x = 0; + if(x) { + goto* &&a; + } + if(x) { + goto* &&b; + } EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); } @@ -39,11 +47,12 @@ TEST(RawTrace, Basic) { #ifndef _MSC_VER raw_trace_basic_precise(); #endif + [[maybe_unused]] volatile int x = 0; // prevent raw_trace_basic_precise() above being a jmp } -CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_2( +CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_2( cpptrace::frame_ptr parent_low_bound, cpptrace::frame_ptr parent_high_bound ) { @@ -54,7 +63,7 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_2( EXPECT_LE(raw_trace.frames[1], parent_high_bound); } -CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_1() { +CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_1() { auto raw_trace = cpptrace::generate_raw_trace(); raw_trace_multi_2(reinterpret_cast(raw_trace_multi_1), reinterpret_cast(raw_trace_multi_1) + 300); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1)); @@ -63,8 +72,8 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_1() { std::vector> parents; -CPPTRACE_FORCE_NO_INLINE void record_parent(cpptrace::frame_ptr low_bound, cpptrace::frame_ptr high_bound) { - parents.insert(parents.begin(), {reinterpret_cast(low_bound), reinterpret_cast(high_bound)}); +CPPTRACE_FORCE_NO_INLINE void record_parent(uintptr_t low_bound, uintptr_t high_bound) { + parents.insert(parents.begin(), {low_bound, high_bound}); } #ifndef _MSC_VER @@ -72,6 +81,13 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_3() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: + volatile auto x = 0; + if(x) { + goto* &&a; + } + if(x) { + goto* &&b; + } EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); // this frame EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); for(size_t i = 0; i < parents.size(); i++) { // parent frames @@ -84,6 +100,13 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: + volatile auto x = 0; + if(x) { + goto* &&a; + } + if(x) { + goto* &&b; + } EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); // this frame EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); for(size_t i = 0; i < parents.size(); i++) { // parent frames @@ -93,19 +116,38 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2() { record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: raw_trace_multi_precise_3(); - d:; + d: + if(x) { + goto* &&c; + } + if(x) { + goto* &&d; + } } CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_1() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: + volatile auto x = 0; + if(x) { + goto* &&a; + } + if(x) { + goto* &&b; + } EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: raw_trace_multi_precise_2(); - d:; + d: + if(x) { + goto* &&c; + } + if(x) { + goto* &&d; + } } #endif diff --git a/test/unit/stacktrace.cpp b/test/unit/stacktrace.cpp index 7160877..87cd389 100644 --- a/test/unit/stacktrace.cpp +++ b/test/unit/stacktrace.cpp @@ -42,7 +42,9 @@ TEST(Stacktrace, Basic) { -CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_3(std::vector& line_numbers) { +// NOTE: returning something and then return stacktrace_multi_3(line_numbers) * 2; later helps prevent the call from +// being optimized to a jmp +CPPTRACE_FORCE_NO_INLINE int stacktrace_multi_3(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto trace = cpptrace::generate_trace(); int i = 0; @@ -61,16 +63,17 @@ CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_3(std::vector& line_numbers) EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_MultipleFrames_Test::TestBody")); + return 2; } -CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_2(std::vector& line_numbers) { +CPPTRACE_FORCE_NO_INLINE int stacktrace_multi_2(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); - stacktrace_multi_3(line_numbers); + return stacktrace_multi_3(line_numbers) * 2; } -CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_1(std::vector& line_numbers) { +CPPTRACE_FORCE_NO_INLINE int stacktrace_multi_1(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); - stacktrace_multi_2(line_numbers); + return stacktrace_multi_2(line_numbers) * 2; } TEST(Stacktrace, MultipleFrames) { @@ -121,7 +124,8 @@ TEST(Stacktrace, RawTraceResolution) { -CPPTRACE_FORCE_NO_INLINE void stacktrace_inline_resolution_3(std::vector& line_numbers) { +#ifndef _MSC_VER +CPPTRACE_FORCE_NO_INLINE int stacktrace_inline_resolution_3(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto trace = cpptrace::generate_trace(); int i = 0; @@ -152,16 +156,17 @@ CPPTRACE_FORCE_NO_INLINE void stacktrace_inline_resolution_3(std::vector& l EXPECT_FALSE(trace.frames[i].is_inline); EXPECT_NE(trace.frames[i].raw_address, 0); EXPECT_NE(trace.frames[i].object_address, 0); + return 2; } -CPPTRACE_FORCE_INLINE void stacktrace_inline_resolution_2(std::vector& line_numbers) { +CPPTRACE_FORCE_INLINE int stacktrace_inline_resolution_2(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); - stacktrace_inline_resolution_3(line_numbers); + return stacktrace_inline_resolution_3(line_numbers) * 2; } -CPPTRACE_FORCE_NO_INLINE void stacktrace_inline_resolution_1(std::vector& line_numbers) { +CPPTRACE_FORCE_NO_INLINE int stacktrace_inline_resolution_1(std::vector& line_numbers) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); - stacktrace_inline_resolution_2(line_numbers); + return stacktrace_inline_resolution_2(line_numbers) * 2; } TEST(Stacktrace, InlineResolution) { @@ -169,3 +174,4 @@ TEST(Stacktrace, InlineResolution) { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_inline_resolution_1(line_numbers); } +#endif