From f7bfe6a5fb9266cb9431a6774211d52f734e6e06 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Wed, 9 Sep 2026 18:02:20 +0800 Subject: [PATCH 1/6] ci: preserve core dump artifacts --- .github/workflows/build_and_test.yaml | 18 ++++++++++++++---- build_support/run-test.sh | 8 ++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index 0c8baf82a..c4602bfff 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -118,10 +118,20 @@ jobs: env: CC: ${{ matrix.cc || 'clang' }} CXX: ${{ matrix.cxx || 'clang++' }} - run: >- - ci/scripts/build_paimon.sh - --source_dir "$(pwd)" - ${{ matrix.build_args }} + run: | + sudo sysctl -w kernel.core_pattern='core.%e.%p' + ulimit -c unlimited + ci/scripts/build_paimon.sh \ + --source_dir "$(pwd)" \ + ${{ matrix.build_args }} - name: Show ccache statistics if: always() run: ccache -s + - name: Upload core dump artifacts + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: core-dumps-${{ matrix.name }} + path: build/build/test-debug/ + if-no-files-found: ignore + retention-days: 7 diff --git a/build_support/run-test.sh b/build_support/run-test.sh index 0656940fd..2b13bd5f9 100755 --- a/build_support/run-test.sh +++ b/build_support/run-test.sh @@ -147,6 +147,10 @@ function print_coredumps() { if [ -n "$COREFILES" ]; then echo "Found core dump, printing backtrace:" + # Preserve the exact executable that produced the core so the dump can be + # inspected offline with matching symbols. + cp "${TEST_EXECUTABLE}" "${TEST_DEBUGDIR}/${TEST_FILENAME}" + for COREFILE in $COREFILES; do # Print backtrace if [ "$(uname)" == "Darwin" ]; then @@ -154,8 +158,8 @@ function print_coredumps() { else gdb -c "${COREFILE}" $TEST_EXECUTABLE -ex "thread apply all bt" -ex "set pagination 0" -batch fi - # Remove the coredump, regenerate it via running the test case directly - rm "${COREFILE}" + # Move the coredump out of the test work directory so CI can upload it. + mv "${COREFILE}" "${TEST_DEBUGDIR}/${TEST_NAME}.${COREFILE}" done fi } From 96ba06cebf7023793da79fe28386deec29c8e152 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Wed, 9 Sep 2026 23:48:33 +0800 Subject: [PATCH 2/6] ci: make core dump artifacts self-contained --- .github/workflows/build_and_test.yaml | 3 + build_support/run-test.sh | 36 ++++++-- ci/scripts/test_coredump_handling.sh | 125 ++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 8 deletions(-) create mode 100755 ci/scripts/test_coredump_handling.sh diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index c4602bfff..758731266 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -49,6 +49,9 @@ jobs: - name: Run asan_symbolize tests shell: bash run: ci/scripts/test_asan_symbolize.sh + - name: Run core dump handling tests + shell: bash + run: ci/scripts/test_coredump_handling.sh build-and-test: name: ${{ matrix.name }} diff --git a/build_support/run-test.sh b/build_support/run-test.sh index 2b13bd5f9..ad9ed0ecd 100755 --- a/build_support/run-test.sh +++ b/build_support/run-test.sh @@ -78,16 +78,17 @@ function setup_sanitizers() { TSAN_OPTIONS="$TSAN_OPTIONS detect_deadlocks=0" TSAN_OPTIONS="$TSAN_OPTIONS suppressions=$ROOT/build_support/tsan-suppressions.txt" TSAN_OPTIONS="$TSAN_OPTIONS history_size=7" + TSAN_OPTIONS="$TSAN_OPTIONS disable_coredump=0" + TSAN_OPTIONS="$TSAN_OPTIONS abort_on_error=1" export TSAN_OPTIONS UBSAN_OPTIONS="$UBSAN_OPTIONS print_stacktrace=1" UBSAN_OPTIONS="$UBSAN_OPTIONS suppressions=$ROOT/build_support/ubsan-suppressions.txt" export UBSAN_OPTIONS - # Enable leak detection even under LLVM 3.4, where it was disabled by default. - # This flag only takes effect when running an ASAN build. - # ASAN_OPTIONS="$ASAN_OPTIONS detect_leaks=1" - # export ASAN_OPTIONS + ASAN_OPTIONS="$ASAN_OPTIONS disable_coredump=0" + ASAN_OPTIONS="$ASAN_OPTIONS abort_on_error=1" + export ASAN_OPTIONS # Set up suppressions for LeakSanitizer LSAN_OPTIONS="$LSAN_OPTIONS suppressions=$ROOT/build_support/lsan-suppressions.txt" @@ -145,11 +146,30 @@ function print_coredumps() { COREFILES=$(ls | grep $PATTERN) if [ -n "$COREFILES" ]; then + # Successful death tests can leave expected core files. Let the test work + # directory cleanup remove those instead of treating them as failures. + if [ "$STATUS" -eq "0" ]; then + return + fi + echo "Found core dump, printing backtrace:" - # Preserve the exact executable that produced the core so the dump can be - # inspected offline with matching symbols. - cp "${TEST_EXECUTABLE}" "${TEST_DEBUGDIR}/${TEST_FILENAME}" + # Keep each test's files separate because tests run in parallel and often + # depend on shared libraries with the same names. + TEST_ARTIFACT_DIR="${TEST_DEBUGDIR}/${TEST_NAME}" + mkdir -p "${TEST_ARTIFACT_DIR}" + cp "${TEST_EXECUTABLE}" "${TEST_ARTIFACT_DIR}/${TEST_FILENAME}" + + # Preserve build-tree shared libraries so downloaded cores can be + # symbolized with `set solib-search-path /lib`. + if [ "$(uname)" != "Darwin" ] && command -v ldd >/dev/null; then + mkdir -p "${TEST_ARTIFACT_DIR}/lib" + while IFS= read -r LIB; do + cp -L "${LIB}" "${TEST_ARTIFACT_DIR}/lib/$(basename "${LIB}")" + done < <(ldd "${TEST_EXECUTABLE}" | awk -v output_root="${OUTPUT_ROOT}/" \ + '$3 ~ /^\// && index($3, output_root) == 1 { print $3; next } + $1 ~ /^\// && index($1, output_root) == 1 { print $1 }') + fi for COREFILE in $COREFILES; do # Print backtrace @@ -159,7 +179,7 @@ function print_coredumps() { gdb -c "${COREFILE}" $TEST_EXECUTABLE -ex "thread apply all bt" -ex "set pagination 0" -batch fi # Move the coredump out of the test work directory so CI can upload it. - mv "${COREFILE}" "${TEST_DEBUGDIR}/${TEST_NAME}.${COREFILE}" + mv "${COREFILE}" "${TEST_ARTIFACT_DIR}/${TEST_NAME}.${COREFILE}" done fi } diff --git a/ci/scripts/test_coredump_handling.sh b/ci/scripts/test_coredump_handling.sh new file mode 100755 index 000000000..4a11dcef4 --- /dev/null +++ b/ci/scripts/test_coredump_handling.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -euo pipefail + +source_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +run_test="${source_dir}/build_support/run-test.sh" +cc=${CC:-cc} + +work_dir=$(mktemp -d) +trap 'rm -rf "${work_dir}"' EXIT + +output_root="${work_dir}/output" +binary_dir="${output_root}/debug" +mkdir -p "${binary_dir}" "${work_dir}/test/test_data" "${work_dir}/bin" + +cat > "${work_dir}/sample.c" <<'EOF' +int sample_value(void) { + return 42; +} +EOF + +cat > "${work_dir}/absolute.c" <<'EOF' +int absolute_value(void) { + return 24; +} +EOF + +cat > "${work_dir}/core_test.c" <<'EOF' +#include +#include +#include + +int sample_value(void); +int absolute_value(void); + +int main(int argc, char** argv) { + const char* executable = strrchr(argv[0], '/'); + executable = executable == NULL ? argv[0] : executable + 1; + + char core_file[256]; + snprintf(core_file, sizeof(core_file), "core.%s.123", executable); + FILE* file = fopen(core_file, "w"); + if (file == NULL) { + return 2; + } + fputs("fake core", file); + fclose(file); + + const char* asan_options = getenv("ASAN_OPTIONS"); + const char* tsan_options = getenv("TSAN_OPTIONS"); + if (sample_value() != 42 || absolute_value() != 24 || asan_options == NULL || + tsan_options == NULL || strstr(asan_options, "disable_coredump=0") == NULL || + strstr(asan_options, "abort_on_error=1") == NULL || + strstr(tsan_options, "disable_coredump=0") == NULL || + strstr(tsan_options, "abort_on_error=1") == NULL) { + return 3; + } + + return argc > 1 && strcmp(argv[1], "success") == 0 ? 0 : 1; +} +EOF + +"${cc}" -fPIC -shared "${work_dir}/sample.c" -o "${binary_dir}/libsample.so" +"${cc}" -fPIC -shared "${work_dir}/absolute.c" -o "${binary_dir}/libabsolute.so" +"${cc}" "${work_dir}/core_test.c" -L"${binary_dir}" -lsample \ + "${binary_dir}/libabsolute.so" -Wl,-rpath,'$ORIGIN' -o "${binary_dir}/failing-test" +cp "${binary_dir}/failing-test" "${binary_dir}/success-test" + +# The fake core is sufficient to exercise collection. Avoid depending on GDB +# being installed or asking it to parse deliberately invalid input. +cat > "${work_dir}/bin/gdb" <<'EOF' +#!/usr/bin/env bash +exit 0 +EOF +chmod +x "${work_dir}/bin/gdb" + +if PATH="${work_dir}/bin:${PATH}" \ + "${run_test}" "${output_root}" test "${binary_dir}/failing-test"; then + echo "run-test.sh unexpectedly reported that the failing test passed" + exit 1 +fi + +artifact_dir="${output_root}/build/test-debug/failing-test" +expected_files=( + "${artifact_dir}/failing-test" + "${artifact_dir}/failing-test.core.failing-test.123" + "${artifact_dir}/lib/libabsolute.so" + "${artifact_dir}/lib/libsample.so" +) +for expected_file in "${expected_files[@]}"; do + if [[ ! -f "${expected_file}" ]]; then + echo "Missing core dump artifact: ${expected_file}" + exit 1 + fi +done + +PATH="${work_dir}/bin:${PATH}" \ + "${run_test}" "${output_root}" test "${binary_dir}/success-test" success + +if [[ -e "${output_root}/build/test-debug/success-test" ]]; then + echo "A successful test preserved an expected core dump" + exit 1 +fi +if [[ -e "${output_root}/build/test-work/success-test" ]]; then + echo "The successful test work directory was not cleaned" + exit 1 +fi + +echo "Core dump handling checks passed" From 7cf7fb64c89373e62f0a603bd87b3fecbf2a7725 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Thu, 10 Sep 2026 13:11:18 +0800 Subject: [PATCH 3/6] ci: only print core dump backtraces --- .github/workflows/build_and_test.yaml | 8 --- build_support/run-test.sh | 27 +--------- ci/scripts/test_coredump_handling.sh | 68 ++++++++----------------- src/paimon/common/utils/status_test.cpp | 12 ----- 4 files changed, 24 insertions(+), 91 deletions(-) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index 758731266..6a41ac772 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -130,11 +130,3 @@ jobs: - name: Show ccache statistics if: always() run: ccache -s - - name: Upload core dump artifacts - if: failure() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: core-dumps-${{ matrix.name }} - path: build/build/test-debug/ - if-no-files-found: ignore - retention-days: 7 diff --git a/build_support/run-test.sh b/build_support/run-test.sh index ad9ed0ecd..743517c0e 100755 --- a/build_support/run-test.sh +++ b/build_support/run-test.sh @@ -146,31 +146,8 @@ function print_coredumps() { COREFILES=$(ls | grep $PATTERN) if [ -n "$COREFILES" ]; then - # Successful death tests can leave expected core files. Let the test work - # directory cleanup remove those instead of treating them as failures. - if [ "$STATUS" -eq "0" ]; then - return - fi - echo "Found core dump, printing backtrace:" - # Keep each test's files separate because tests run in parallel and often - # depend on shared libraries with the same names. - TEST_ARTIFACT_DIR="${TEST_DEBUGDIR}/${TEST_NAME}" - mkdir -p "${TEST_ARTIFACT_DIR}" - cp "${TEST_EXECUTABLE}" "${TEST_ARTIFACT_DIR}/${TEST_FILENAME}" - - # Preserve build-tree shared libraries so downloaded cores can be - # symbolized with `set solib-search-path /lib`. - if [ "$(uname)" != "Darwin" ] && command -v ldd >/dev/null; then - mkdir -p "${TEST_ARTIFACT_DIR}/lib" - while IFS= read -r LIB; do - cp -L "${LIB}" "${TEST_ARTIFACT_DIR}/lib/$(basename "${LIB}")" - done < <(ldd "${TEST_EXECUTABLE}" | awk -v output_root="${OUTPUT_ROOT}/" \ - '$3 ~ /^\// && index($3, output_root) == 1 { print $3; next } - $1 ~ /^\// && index($1, output_root) == 1 { print $1 }') - fi - for COREFILE in $COREFILES; do # Print backtrace if [ "$(uname)" == "Darwin" ]; then @@ -178,8 +155,8 @@ function print_coredumps() { else gdb -c "${COREFILE}" $TEST_EXECUTABLE -ex "thread apply all bt" -ex "set pagination 0" -batch fi - # Move the coredump out of the test work directory so CI can upload it. - mv "${COREFILE}" "${TEST_ARTIFACT_DIR}/${TEST_NAME}.${COREFILE}" + # Remove the coredump, regenerate it via running the test case directly + rm "${COREFILE}" done fi } diff --git a/ci/scripts/test_coredump_handling.sh b/ci/scripts/test_coredump_handling.sh index 4a11dcef4..cdb8f83ba 100755 --- a/ci/scripts/test_coredump_handling.sh +++ b/ci/scripts/test_coredump_handling.sh @@ -29,26 +29,11 @@ output_root="${work_dir}/output" binary_dir="${output_root}/debug" mkdir -p "${binary_dir}" "${work_dir}/test/test_data" "${work_dir}/bin" -cat > "${work_dir}/sample.c" <<'EOF' -int sample_value(void) { - return 42; -} -EOF - -cat > "${work_dir}/absolute.c" <<'EOF' -int absolute_value(void) { - return 24; -} -EOF - cat > "${work_dir}/core_test.c" <<'EOF' #include #include #include -int sample_value(void); -int absolute_value(void); - int main(int argc, char** argv) { const char* executable = strrchr(argv[0], '/'); executable = executable == NULL ? argv[0] : executable + 1; @@ -64,61 +49,52 @@ int main(int argc, char** argv) { const char* asan_options = getenv("ASAN_OPTIONS"); const char* tsan_options = getenv("TSAN_OPTIONS"); - if (sample_value() != 42 || absolute_value() != 24 || asan_options == NULL || - tsan_options == NULL || strstr(asan_options, "disable_coredump=0") == NULL || + if (asan_options == NULL || tsan_options == NULL || + strstr(asan_options, "disable_coredump=0") == NULL || strstr(asan_options, "abort_on_error=1") == NULL || strstr(tsan_options, "disable_coredump=0") == NULL || strstr(tsan_options, "abort_on_error=1") == NULL) { return 3; } - return argc > 1 && strcmp(argv[1], "success") == 0 ? 0 : 1; + return 1; } EOF -"${cc}" -fPIC -shared "${work_dir}/sample.c" -o "${binary_dir}/libsample.so" -"${cc}" -fPIC -shared "${work_dir}/absolute.c" -o "${binary_dir}/libabsolute.so" -"${cc}" "${work_dir}/core_test.c" -L"${binary_dir}" -lsample \ - "${binary_dir}/libabsolute.so" -Wl,-rpath,'$ORIGIN' -o "${binary_dir}/failing-test" -cp "${binary_dir}/failing-test" "${binary_dir}/success-test" +"${cc}" "${work_dir}/core_test.c" -o "${binary_dir}/failing-test" -# The fake core is sufficient to exercise collection. Avoid depending on GDB +# The fake core is sufficient to exercise backtrace logging. Avoid depending on GDB # being installed or asking it to parse deliberately invalid input. cat > "${work_dir}/bin/gdb" <<'EOF' #!/usr/bin/env bash +printf '%s\n' "$*" > "${GDB_LOG}" exit 0 EOF chmod +x "${work_dir}/bin/gdb" +export GDB_LOG="${work_dir}/gdb.log" +run_output="${work_dir}/run-test.log" if PATH="${work_dir}/bin:${PATH}" \ - "${run_test}" "${output_root}" test "${binary_dir}/failing-test"; then + "${run_test}" "${output_root}" test "${binary_dir}/failing-test" \ + > "${run_output}" 2>&1; then echo "run-test.sh unexpectedly reported that the failing test passed" exit 1 fi -artifact_dir="${output_root}/build/test-debug/failing-test" -expected_files=( - "${artifact_dir}/failing-test" - "${artifact_dir}/failing-test.core.failing-test.123" - "${artifact_dir}/lib/libabsolute.so" - "${artifact_dir}/lib/libsample.so" -) -for expected_file in "${expected_files[@]}"; do - if [[ ! -f "${expected_file}" ]]; then - echo "Missing core dump artifact: ${expected_file}" - exit 1 - fi -done - -PATH="${work_dir}/bin:${PATH}" \ - "${run_test}" "${output_root}" test "${binary_dir}/success-test" success - -if [[ -e "${output_root}/build/test-debug/success-test" ]]; then - echo "A successful test preserved an expected core dump" +if ! grep -q "Found core dump, printing backtrace" "${run_output}"; then + echo "run-test.sh did not report the core dump" + exit 1 +fi +if ! grep -q -- "-ex thread apply all bt" "${GDB_LOG}"; then + echo "run-test.sh did not ask GDB to print all thread backtraces" + exit 1 +fi +if find "${output_root}/build" -name 'core.*' -print -quit | grep -q .; then + echo "run-test.sh preserved a core dump" exit 1 fi -if [[ -e "${output_root}/build/test-work/success-test" ]]; then - echo "The successful test work directory was not cleaned" +if find "${output_root}/build/test-debug" -type f -print -quit | grep -q .; then + echo "run-test.sh preserved a binary artifact" exit 1 fi diff --git a/src/paimon/common/utils/status_test.cpp b/src/paimon/common/utils/status_test.cpp index cd9b60fe5..9c9e6b747 100644 --- a/src/paimon/common/utils/status_test.cpp +++ b/src/paimon/common/utils/status_test.cpp @@ -99,18 +99,6 @@ TEST(StatusTest, TestCodeAsString) { ASSERT_EQ("OK", Status::OK().CodeAsString()); } -TEST(StatusDeathTest, TestAbort) { - // Death tests fork(); with the default "fast" style, forking in a multi-threaded - // process is unsafe and under ThreadSanitizer the child aborts with a sanitizer - // message before Abort() runs. The "threadsafe" style re-execs the test binary in - // a clean process so Abort()'s own output is produced and can be matched. - const std::string prev_style = testing::GTEST_FLAG(death_test_style); - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - ASSERT_DEATH(Status::IOError("boom").Abort(), "Paimon Fatal Error"); - ASSERT_DEATH(Status::IOError("boom").Abort("custom prefix"), "custom prefix"); - testing::GTEST_FLAG(death_test_style) = prev_style; -} - TEST(StatusTest, TestWithDetail) { Status status(StatusCode::IOError, "summary"); auto detail = std::make_shared(); From 0071ca4b1d45f1319a875be851f6dcf0e96ff768 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Thu, 10 Sep 2026 14:14:30 +0800 Subject: [PATCH 4/6] ci: install gdb for core dump backtraces --- .github/workflows/build_and_test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index 6a41ac772..d16e1c1c2 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -112,10 +112,10 @@ jobs: - name: Install Rust toolchain (Mosaic and tantivy-fts) shell: bash run: ci/scripts/setup_rust.sh - - name: Install HTTP and TLS development dependencies + - name: Install build and debugging dependencies run: | sudo apt-get update - sudo apt-get install -y libcurl4-openssl-dev libssl-dev + sudo apt-get install -y gdb libcurl4-openssl-dev libssl-dev - name: Build Paimon shell: bash env: From 0bbcd43a3f27562b49c98cedd73b6072360e32ee Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Thu, 10 Sep 2026 14:16:13 +0800 Subject: [PATCH 5/6] test(ci): exercise real core dump backtraces --- .github/workflows/build_and_test.yaml | 9 +++++- ci/scripts/test_coredump_handling.sh | 43 ++++++++------------------- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index d16e1c1c2..2cb03bc13 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -49,9 +49,16 @@ jobs: - name: Run asan_symbolize tests shell: bash run: ci/scripts/test_asan_symbolize.sh + - name: Install GDB + run: | + sudo apt-get update + sudo apt-get install -y gdb - name: Run core dump handling tests shell: bash - run: ci/scripts/test_coredump_handling.sh + run: | + sudo sysctl -w kernel.core_pattern='core.%e.%p' + ulimit -c unlimited + ci/scripts/test_coredump_handling.sh build-and-test: name: ${{ matrix.name }} diff --git a/ci/scripts/test_coredump_handling.sh b/ci/scripts/test_coredump_handling.sh index cdb8f83ba..24b41bbf3 100755 --- a/ci/scripts/test_coredump_handling.sh +++ b/ci/scripts/test_coredump_handling.sh @@ -27,26 +27,13 @@ trap 'rm -rf "${work_dir}"' EXIT output_root="${work_dir}/output" binary_dir="${output_root}/debug" -mkdir -p "${binary_dir}" "${work_dir}/test/test_data" "${work_dir}/bin" +mkdir -p "${binary_dir}" "${work_dir}/test/test_data" cat > "${work_dir}/core_test.c" <<'EOF' -#include #include #include -int main(int argc, char** argv) { - const char* executable = strrchr(argv[0], '/'); - executable = executable == NULL ? argv[0] : executable + 1; - - char core_file[256]; - snprintf(core_file, sizeof(core_file), "core.%s.123", executable); - FILE* file = fopen(core_file, "w"); - if (file == NULL) { - return 2; - } - fputs("fake core", file); - fclose(file); - +int main(void) { const char* asan_options = getenv("ASAN_OPTIONS"); const char* tsan_options = getenv("TSAN_OPTIONS"); if (asan_options == NULL || tsan_options == NULL || @@ -57,36 +44,30 @@ int main(int argc, char** argv) { return 3; } - return 1; + abort(); } EOF -"${cc}" "${work_dir}/core_test.c" -o "${binary_dir}/failing-test" - -# The fake core is sufficient to exercise backtrace logging. Avoid depending on GDB -# being installed or asking it to parse deliberately invalid input. -cat > "${work_dir}/bin/gdb" <<'EOF' -#!/usr/bin/env bash -printf '%s\n' "$*" > "${GDB_LOG}" -exit 0 -EOF -chmod +x "${work_dir}/bin/gdb" +"${cc}" -g -O0 "${work_dir}/core_test.c" -o "${binary_dir}/failing-test" -export GDB_LOG="${work_dir}/gdb.log" run_output="${work_dir}/run-test.log" -if PATH="${work_dir}/bin:${PATH}" \ - "${run_test}" "${output_root}" test "${binary_dir}/failing-test" \ +if "${run_test}" "${output_root}" test "${binary_dir}/failing-test" \ > "${run_output}" 2>&1; then echo "run-test.sh unexpectedly reported that the failing test passed" exit 1 fi +cat "${run_output}" if ! grep -q "Found core dump, printing backtrace" "${run_output}"; then echo "run-test.sh did not report the core dump" exit 1 fi -if ! grep -q -- "-ex thread apply all bt" "${GDB_LOG}"; then - echo "run-test.sh did not ask GDB to print all thread backtraces" +if ! grep -q "Program terminated with signal SIGABRT" "${run_output}"; then + echo "GDB did not report the abort signal" + exit 1 +fi +if ! grep -Eq '#[0-9]+[[:space:]]+.*main' "${run_output}"; then + echo "GDB did not print the crashing main frame" exit 1 fi if find "${output_root}/build" -name 'core.*' -print -quit | grep -q .; then From 77e4d094673d69011949a1c67e597b1734b44835 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Thu, 10 Sep 2026 15:02:11 +0800 Subject: [PATCH 6/6] test(ci): remove one-shot core dump test --- .github/workflows/build_and_test.yaml | 10 ---- ci/scripts/test_coredump_handling.sh | 82 --------------------------- 2 files changed, 92 deletions(-) delete mode 100755 ci/scripts/test_coredump_handling.sh diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index 2cb03bc13..e31bebf2a 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -49,16 +49,6 @@ jobs: - name: Run asan_symbolize tests shell: bash run: ci/scripts/test_asan_symbolize.sh - - name: Install GDB - run: | - sudo apt-get update - sudo apt-get install -y gdb - - name: Run core dump handling tests - shell: bash - run: | - sudo sysctl -w kernel.core_pattern='core.%e.%p' - ulimit -c unlimited - ci/scripts/test_coredump_handling.sh build-and-test: name: ${{ matrix.name }} diff --git a/ci/scripts/test_coredump_handling.sh b/ci/scripts/test_coredump_handling.sh deleted file mode 100755 index 24b41bbf3..000000000 --- a/ci/scripts/test_coredump_handling.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -set -euo pipefail - -source_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) -run_test="${source_dir}/build_support/run-test.sh" -cc=${CC:-cc} - -work_dir=$(mktemp -d) -trap 'rm -rf "${work_dir}"' EXIT - -output_root="${work_dir}/output" -binary_dir="${output_root}/debug" -mkdir -p "${binary_dir}" "${work_dir}/test/test_data" - -cat > "${work_dir}/core_test.c" <<'EOF' -#include -#include - -int main(void) { - const char* asan_options = getenv("ASAN_OPTIONS"); - const char* tsan_options = getenv("TSAN_OPTIONS"); - if (asan_options == NULL || tsan_options == NULL || - strstr(asan_options, "disable_coredump=0") == NULL || - strstr(asan_options, "abort_on_error=1") == NULL || - strstr(tsan_options, "disable_coredump=0") == NULL || - strstr(tsan_options, "abort_on_error=1") == NULL) { - return 3; - } - - abort(); -} -EOF - -"${cc}" -g -O0 "${work_dir}/core_test.c" -o "${binary_dir}/failing-test" - -run_output="${work_dir}/run-test.log" -if "${run_test}" "${output_root}" test "${binary_dir}/failing-test" \ - > "${run_output}" 2>&1; then - echo "run-test.sh unexpectedly reported that the failing test passed" - exit 1 -fi -cat "${run_output}" - -if ! grep -q "Found core dump, printing backtrace" "${run_output}"; then - echo "run-test.sh did not report the core dump" - exit 1 -fi -if ! grep -q "Program terminated with signal SIGABRT" "${run_output}"; then - echo "GDB did not report the abort signal" - exit 1 -fi -if ! grep -Eq '#[0-9]+[[:space:]]+.*main' "${run_output}"; then - echo "GDB did not print the crashing main frame" - exit 1 -fi -if find "${output_root}/build" -name 'core.*' -print -quit | grep -q .; then - echo "run-test.sh preserved a core dump" - exit 1 -fi -if find "${output_root}/build/test-debug" -type f -print -quit | grep -q .; then - echo "run-test.sh preserved a binary artifact" - exit 1 -fi - -echo "Core dump handling checks passed"