aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-24 13:18:12 +0100
committerGitHub Enterprise <[email protected]>2026-03-24 13:18:12 +0100
commitfec7fea085968bb93291638fd6d8c037682595ec (patch)
tree1871b36ead5b8fea3db12a347e658344afedf572
parentrefactor hub notifications (#888) (diff)
downloadzen-fec7fea085968bb93291638fd6d8c037682595ec.tar.xz
zen-fec7fea085968bb93291638fd6d8c037682595ec.zip
Linux Crashpad fix (#890)
- **Replace crashpad static-libc++ patch file with `io.replace()` in `on_install`** — The old `.patch` file was fragile (trailing-whitespace stripping on Windows would silently break it). Using `io.replace()` in the xmake build script is more robust and easier to maintain. - **Skip sentry-native `on_test` link check on Linux** — The link test requires `-lc++abi` when building with the UE clang toolchain but adding it unconditionally breaks GCC/libstdc++ builds. The zenserver build itself validates that the library is usable. - **Add `crashpad-test.sh`** — A test script that launches a release zenserver, waits for the health endpoint, then verifies that `crashpad_handler` is running, no `sentry_init` failure was logged, and the handler has no dynamic `libc++.so.1` dependency. - **Add Crashpad Check step to Linux release CI** — Runs `crashpad-test.sh` in the `validate` workflow for release builds to catch crashpad regressions before merge.
-rw-r--r--.github/workflows/validate.yml6
-rw-r--r--repo/packages/s/sentry-native/patches/0.12.1/crashpad_static_libcxx.patch25
-rw-r--r--repo/packages/s/sentry-native/xmake.lua37
-rwxr-xr-xscripts/test_linux/crashpad-test.sh184
4 files changed, 226 insertions, 26 deletions
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 932020337..601f000fe 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -178,6 +178,12 @@ jobs:
run: |
xmake bundle -v -y
+ - name: Crashpad Check
+ if: ${{ matrix.config == 'release' }}
+ shell: bash
+ run: |
+ ./scripts/test_linux/crashpad-test.sh build/linux/${{ matrix.arch }}/release/zenserver
+
- name: Upload zenserver-linux
if: ${{ github.ref_name == 'main' && matrix.config == 'release' }}
uses: actions/upload-artifact@v3-node20
diff --git a/repo/packages/s/sentry-native/patches/0.12.1/crashpad_static_libcxx.patch b/repo/packages/s/sentry-native/patches/0.12.1/crashpad_static_libcxx.patch
deleted file mode 100644
index 64458821d..000000000
--- a/repo/packages/s/sentry-native/patches/0.12.1/crashpad_static_libcxx.patch
+++ /dev/null
@@ -1,25 +0,0 @@
---- a/external/crashpad/handler/CMakeLists.txt 2026-03-09 14:47:42.109197582 +0000
-+++ b/external/crashpad/handler/CMakeLists.txt 2026-03-09 14:51:45.343538268 +0000
-@@ -120,6 +120,22 @@
- endif()
- endif()
-
-+ # Statically link libc++ and libc++abi into crashpad_handler so it has
-+ # no runtime dependency on libc++.so.1. This is needed when building with
-+ # a toolchain that uses libc++ (e.g. UE clang) but deploys to systems
-+ # where libc++.so.1 is not available.
-+ # Only applied when -stdlib=libc++ is active (i.e. not GCC or system clang
-+ # using libstdc++).
-+ if(LINUX)
-+ string(FIND "${CMAKE_CXX_FLAGS}" "-stdlib=libc++" _libcxx_pos)
-+ if(NOT _libcxx_pos EQUAL -1)
-+ target_link_options(crashpad_handler PRIVATE -nostdlib++)
-+ target_link_libraries(crashpad_handler PRIVATE
-+ -Wl,-Bstatic,-lc++,-lc++abi,-Bdynamic
-+ )
-+ endif()
-+ endif()
-+
- set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME crashpad_handler)
- add_executable(crashpad::handler ALIAS crashpad_handler)
-
diff --git a/repo/packages/s/sentry-native/xmake.lua b/repo/packages/s/sentry-native/xmake.lua
index 59418c835..2d7ab7e59 100644
--- a/repo/packages/s/sentry-native/xmake.lua
+++ b/repo/packages/s/sentry-native/xmake.lua
@@ -37,9 +37,11 @@ package("sentry-native")
add_versions("0.4.4", "fe6c711d42861e66e53bfd7ee0b2b226027c64446857f0d1bbb239ca824a3d8d")
add_patches("0.4.4", path.join(os.scriptdir(), "patches", "0.4.4", "zlib_fix.patch"), "1a6ac711b7824112a9062ec1716a316facce5055498d1f87090d2cad031b865b")
add_patches("0.7.6", path.join(os.scriptdir(), "patches", "0.7.6", "breakpad_exceptions.patch"), "7781bad0404a92252cbad39e865d17ac663eedade03cbd29c899636c7bfab1b5")
- add_patches("0.12.1", path.join(os.scriptdir(), "patches", "0.12.1", "crashpad_static_libcxx.patch"), "5efa0e7b106b9fc121f819f305086f7f3a2b8aa519a052bf82ea60d86c15872d")
add_patches("0.12.1", path.join(os.scriptdir(), "patches", "0.12.1", "breakpad_exceptions.patch"), "9e0cd152192f87b9ce182c8ddff22c0471acb99bd61a872ca48afbbacdf27575")
+ -- Internal: bump this value to invalidate the package cache after build-logic changes.
+ add_configs("build_patch", {description = "Internal cache-buster for build logic changes", default = "3", type = "string"})
+
add_deps("cmake")
add_configs("backend", {description = "Set the backend of sentry to use", type = "string"})
@@ -113,11 +115,44 @@ package("sentry-native")
elseif package:is_plat("macosx") then
opt.shflags = {"-framework", "SystemConfiguration"}
table.insert(configs, "-DCMAKE_OSX_SYSROOT=" .. os.iorun("xcrun --show-sdk-path"))
+ elseif package:is_plat("linux") then
+ -- Statically link libc++ and libc++abi into crashpad_handler so it has no runtime
+ -- dependency on libc++.so.1, which may not be present on all Linux deployments.
+ -- Conditioned on -stdlib=libc++ being active so GCC and system-clang builds
+ -- (which use libstdc++) are unaffected.
+ -- Done via io.replace() rather than a patch file to avoid fragility from trailing-
+ -- whitespace stripping (a common issue with Windows-generated patch files).
+ local handler_cmake = path.join(os.curdir(), "external/crashpad/handler/CMakeLists.txt")
+ if os.isfile(handler_cmake) then
+ io.replace(handler_cmake,
+ " set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME crashpad_handler)",
+ table.concat({
+ " if(LINUX)",
+ " string(FIND \"${CMAKE_CXX_FLAGS}\" \"-stdlib=libc++\" _libcxx_pos)",
+ " if(NOT _libcxx_pos EQUAL -1)",
+ " target_link_options(crashpad_handler PRIVATE -nostdlib++)",
+ " target_link_libraries(crashpad_handler PRIVATE",
+ " -Wl,-Bstatic,-lc++,-lc++abi,-Bdynamic",
+ " )",
+ " endif()",
+ " endif()",
+ "",
+ " set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME crashpad_handler)",
+ }, "\n"),
+ {plain = true})
+ end
end
import("package.tools.cmake").install(package, configs, opt)
end)
on_test(function (package)
+ -- On Linux the link test requires libc++abi when building with the UE clang toolchain
+ -- (-stdlib=libc++ implies -lc++ but not -lc++abi), and adding it unconditionally would
+ -- break GCC/libstdc++ builds. Skip the link check on Linux; the zenserver build itself
+ -- validates that the library is usable.
+ if package:is_plat("linux") then
+ return
+ end
assert(package:check_cxxsnippets({test = [[
void test(int args, char** argv) {
sentry_options_t* options = sentry_options_new();
diff --git a/scripts/test_linux/crashpad-test.sh b/scripts/test_linux/crashpad-test.sh
new file mode 100755
index 000000000..bd08cefc5
--- /dev/null
+++ b/scripts/test_linux/crashpad-test.sh
@@ -0,0 +1,184 @@
+#!/usr/bin/env bash
+# Verify that crashpad_handler is active when zenserver (release build) starts.
+#
+# This test:
+# 1. Launches zenserver from the release build.
+# 2. Waits for the HTTP health endpoint to become ready.
+# 3. Checks that a crashpad_handler child process is running.
+# 4. Checks that the startup log contains "sentry initialized" (not a failure).
+#
+# Usage:
+# ./scripts/test_linux/crashpad-test.sh [path-to-zenserver]
+#
+# If no path is given, defaults to build/linux/x86_64/release/zenserver
+# relative to the repository root.
+#
+# The test exits 0 on success, 1 on failure.
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+
+ZENSERVER_BINARY="${1:-$REPO_ROOT/build/linux/x86_64/release/zenserver}"
+CRASHPAD_HANDLER="$(dirname "$ZENSERVER_BINARY")/crashpad_handler"
+PORT=18558 # Use a non-default port so we don't collide with a running zenserver
+DATA_DIR="$(mktemp -d)"
+STDOUT_FILE="$DATA_DIR/stdout.log"
+ZENSERVER_PID=""
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+PASSED=0
+FAILED=0
+
+pass() {
+ echo -e " ${GREEN}PASS${NC} $1"
+ (( PASSED++ )) || true
+}
+
+fail() {
+ echo -e " ${RED}FAIL${NC} $1"
+ (( FAILED++ )) || true
+}
+
+cleanup() {
+ set +e
+ if [ -n "$ZENSERVER_PID" ] && kill -0 "$ZENSERVER_PID" 2>/dev/null; then
+ kill "$ZENSERVER_PID" 2>/dev/null
+ wait "$ZENSERVER_PID" 2>/dev/null
+ fi
+ rm -rf "$DATA_DIR"
+}
+trap cleanup EXIT
+
+# ── Preflight ────────────────────────────────────────────────────────────────
+
+echo ""
+echo "=============================="
+echo " Crashpad active check"
+echo "=============================="
+echo ""
+
+if [ ! -f "$ZENSERVER_BINARY" ]; then
+ echo -e "${RED}ERROR: zenserver binary not found: $ZENSERVER_BINARY${NC}"
+ echo " Build with: xmake config -m release && xmake build zenserver"
+ exit 1
+fi
+
+if [ ! -f "$CRASHPAD_HANDLER" ]; then
+ echo -e "${RED}ERROR: crashpad_handler not found alongside zenserver: $CRASHPAD_HANDLER${NC}"
+ echo " It should be copied there automatically by the build."
+ exit 1
+fi
+
+echo "zenserver: $ZENSERVER_BINARY"
+echo "crashpad_handler: $CRASHPAD_HANDLER"
+echo "port: $PORT"
+echo "data dir: $DATA_DIR"
+echo ""
+
+# ── Start zenserver ──────────────────────────────────────────────────────────
+
+# zenserver runs in the foreground until SIGINT/SIGTERM. Launch in background
+# so we can poll its health endpoint and inspect child processes.
+"$ZENSERVER_BINARY" \
+ --port="$PORT" \
+ --data-dir="$DATA_DIR" \
+ > "$STDOUT_FILE" 2>&1 &
+ZENSERVER_PID=$!
+
+echo "Started zenserver (pid $ZENSERVER_PID), waiting for health endpoint..."
+
+# ── Wait for health endpoint ─────────────────────────────────────────────────
+
+READY=false
+for i in $(seq 1 40); do
+ if curl -sf "http://localhost:$PORT/health" > /dev/null 2>&1; then
+ READY=true
+ break
+ fi
+ sleep 0.5
+done
+
+if [ "$READY" = false ]; then
+ echo -e "${RED}ERROR: zenserver did not become ready within 20 seconds${NC}"
+ if [ -f "$STDOUT_FILE" ]; then
+ echo ""
+ echo "--- stdout ---"
+ cat "$STDOUT_FILE"
+ fi
+ exit 1
+fi
+
+echo "Server is ready."
+
+# Give the server a moment to finish startup logging (sentry init log
+# message is emitted after the health endpoint comes up).
+sleep 1
+echo ""
+
+# ── Test 1: crashpad_handler process is running for our data dir ─────────────
+#
+# sentry-native starts crashpad_handler with --database pointing at
+# $DATA_DIR/.sentry-native. The process re-parents itself out of zenserver's
+# process tree, so we look for it by its command-line arguments rather than
+# by parent PID.
+
+HANDLER_PID="$(pgrep -f "crashpad_handler.*${DATA_DIR}" 2>/dev/null | head -1 || true)"
+if [ -n "$HANDLER_PID" ]; then
+ pass "crashpad_handler is running (pid $HANDLER_PID) with database in our data dir"
+else
+ fail "crashpad_handler process not found — sentry_init may have failed or sentry is disabled"
+fi
+
+# ── Test 2: No sentry_init failure in startup log ────────────────────────────
+#
+# The "sentry initialized" success message is logged at INFO level under the
+# sentry-sdk log category, which is filtered to Warn by default — so it won't
+# appear in normal stdout. We check for the absence of the failure message
+# instead (which IS at Warn level and would appear if sentry_init failed).
+
+if grep -q "sentry_init returned failure" "$STDOUT_FILE" 2>/dev/null; then
+ ERRMSG="$(grep "sentry_init returned failure" "$STDOUT_FILE" | head -1)"
+ fail "sentry_init reported failure: $ERRMSG"
+else
+ pass "No sentry_init failure message in startup log"
+fi
+
+# ── Test 3: ldd sanity — crashpad_handler must not need libc++.so.1 ──────────
+
+MISSING_LIBCXX="$(ldd "$CRASHPAD_HANDLER" 2>/dev/null | grep "libc++\.so\.1" | grep "not found" || true)"
+if [ -n "$MISSING_LIBCXX" ]; then
+ fail "crashpad_handler has an unsatisfied libc++.so.1 dependency (static linking patch not applied)"
+elif ldd "$CRASHPAD_HANDLER" 2>/dev/null | grep -q "libc++\.so\.1"; then
+ fail "crashpad_handler links libc++.so.1 dynamically — it should be statically linked"
+else
+ pass "crashpad_handler has no dynamic libc++.so.1 dependency"
+fi
+
+# ── Summary ──────────────────────────────────────────────────────────────────
+
+echo ""
+echo "=============================="
+printf " Passed: "
+echo -e "${GREEN}${PASSED}${NC}"
+printf " Failed: "
+if [ "$FAILED" -gt 0 ]; then
+ echo -e "${RED}${FAILED}${NC}"
+else
+ echo -e "${GREEN}${FAILED}${NC}"
+fi
+echo "=============================="
+echo ""
+
+if [ "$FAILED" -gt 0 ]; then
+ if [ -f "$STDOUT_FILE" ]; then
+ echo "--- stdout ---"
+ cat "$STDOUT_FILE"
+ fi
+ exit 1
+fi