diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 27e24ac..461d0b1 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -81,6 +81,19 @@ jobs: name: windows-x64 build_cmd: .\build.cmd release-tests && .\build.cmd release-examples build_dir: build-release + # Sanitizer builds (Linux-only, debug, no examples/artifacts) + - os: ubuntu-latest + name: linux-x64-asan + sanitizer: true + preset: linux-debug-asan + build_dir: build-debug-asan + env_vars: "" + - os: ubuntu-latest + name: linux-x64-tsan + sanitizer: true + preset: linux-debug-tsan + build_dir: build-debug-tsan + env_vars: "TSAN_OPTIONS=suppressions=$GITHUB_WORKSPACE/tsan_suppressions.txt:halt_on_error=1" name: Build (${{ matrix.name }}) runs-on: ${{ matrix.os }} @@ -175,7 +188,7 @@ jobs: # ---------- Build ---------- - name: Build (Unix) - if: runner.os != 'Windows' + if: runner.os != 'Windows' && !matrix.sanitizer shell: bash run: | chmod +x build.sh @@ -186,11 +199,19 @@ jobs: shell: pwsh run: ${{ matrix.build_cmd }} + - name: Configure (sanitizer) + if: matrix.sanitizer + run: cmake --preset ${{ matrix.preset }} + + - name: Build (sanitizer) + if: matrix.sanitizer + run: cmake --build --preset ${{ matrix.preset }} + # ---------- Smoke test cpp-example-collection binaries ---------- # Built under cpp-example-collection-build/ (not build-dir/bin). Visual Studio # multi-config places executables in per-target Release/ (or Debug/) subdirs. - name: Smoke test examples (Unix) - if: runner.os != 'Windows' + if: runner.os != 'Windows' && !matrix.sanitizer shell: bash run: | set -x @@ -288,9 +309,17 @@ jobs: # ---------- Run unit tests ---------- - name: Run unit tests (Unix) - if: runner.os != 'Windows' + if: runner.os != 'Windows' && !matrix.sanitizer + shell: bash + run: | + ${{ matrix.build_dir }}/bin/livekit_unit_tests \ + --gtest_output=xml:${{ matrix.build_dir }}/unit-test-results.xml + + - name: Run unit tests (sanitizer) + if: matrix.sanitizer shell: bash run: | + ${{ matrix.env_vars }} \ ${{ matrix.build_dir }}/bin/livekit_unit_tests \ --gtest_output=xml:${{ matrix.build_dir }}/unit-test-results.xml @@ -311,6 +340,7 @@ jobs: # ---------- Upload artifacts ---------- - name: Upload build artifacts + if: "!matrix.sanitizer" uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: livekit-sdk-${{ matrix.name }} @@ -325,7 +355,9 @@ jobs: if: always() shell: bash run: | - if [[ "$RUNNER_OS" == "Windows" ]]; then + if [[ -n "${{ matrix.sanitizer }}" ]]; then + rm -rf ${{ matrix.build_dir }} || true + elif [[ "$RUNNER_OS" == "Windows" ]]; then rm -rf build-release build-debug || true else ./build.sh clean-all || true diff --git a/CMakeLists.txt b/CMakeLists.txt index c6f7993..16ffd4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,30 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# ---------- Sanitizer support (Linux only) ---------- +set(LIVEKIT_SANITIZER "" CACHE STRING + "Enable a sanitizer for the C++ code (address, thread, undefined, or leave empty)") +if(LIVEKIT_SANITIZER) + if(WIN32) + message(FATAL_ERROR "LIVEKIT_SANITIZER is not supported on Windows (MSVC lacks TSan/UBSan)") + endif() + set(_SAN_FLAGS "") + if(LIVEKIT_SANITIZER STREQUAL "address") + set(_SAN_FLAGS "-fsanitize=address,undefined -fno-omit-frame-pointer") + elseif(LIVEKIT_SANITIZER STREQUAL "thread") + set(_SAN_FLAGS "-fsanitize=thread") + elseif(LIVEKIT_SANITIZER STREQUAL "undefined") + set(_SAN_FLAGS "-fsanitize=undefined") + else() + message(FATAL_ERROR "Unknown sanitizer: ${LIVEKIT_SANITIZER}. Use address, thread, or undefined.") + endif() + string(APPEND CMAKE_C_FLAGS " ${_SAN_FLAGS}") + string(APPEND CMAKE_CXX_FLAGS " ${_SAN_FLAGS}") + string(APPEND CMAKE_EXE_LINKER_FLAGS " ${_SAN_FLAGS}") + string(APPEND CMAKE_SHARED_LINKER_FLAGS " ${_SAN_FLAGS}") + message(STATUS "Sanitizer enabled: ${LIVEKIT_SANITIZER} (${_SAN_FLAGS})") +endif() + # Set RPATH for Unix systems to find shared libraries in executable directory if(UNIX) if(APPLE) diff --git a/CMakePresets.json b/CMakePresets.json index 5f6b3be..e8198f7 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -278,6 +278,32 @@ "LIVEKIT_BUILD_EXAMPLES": "OFF", "LIVEKIT_BUILD_TESTS": "ON" } + }, + { + "name": "linux-debug-asan", + "displayName": "Linux Debug with AddressSanitizer", + "description": "Build for Linux Debug with ASan + UBSan and tests", + "inherits": "linux-base", + "binaryDir": "${sourceDir}/build-debug-asan", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "LIVEKIT_BUILD_EXAMPLES": "OFF", + "LIVEKIT_BUILD_TESTS": "ON", + "LIVEKIT_SANITIZER": "address" + } + }, + { + "name": "linux-debug-tsan", + "displayName": "Linux Debug with ThreadSanitizer", + "description": "Build for Linux Debug with TSan and tests", + "inherits": "linux-base", + "binaryDir": "${sourceDir}/build-debug-tsan", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "LIVEKIT_BUILD_EXAMPLES": "OFF", + "LIVEKIT_BUILD_TESTS": "ON", + "LIVEKIT_SANITIZER": "thread" + } } ], "buildPresets": [ @@ -358,6 +384,14 @@ { "name": "macos-debug-tests", "configurePreset": "macos-debug-tests" + }, + { + "name": "linux-debug-asan", + "configurePreset": "linux-debug-asan" + }, + { + "name": "linux-debug-tsan", + "configurePreset": "linux-debug-tsan" } ], "testPresets": [ @@ -404,6 +438,20 @@ "output": { "outputOnFailure": true } + }, + { + "name": "linux-debug-asan", + "configurePreset": "linux-debug-asan", + "output": { + "outputOnFailure": true + } + }, + { + "name": "linux-debug-tsan", + "configurePreset": "linux-debug-tsan", + "output": { + "outputOnFailure": true + } } ] } diff --git a/README.md b/README.md index 04b5ecf..ac66f6a 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Use this SDK to add realtime video, audio and data features to your C++ app. By - **Git LFS** (required for examples) Some example data files (e.g., audio assets) are stored using Git LFS. You must install Git LFS before cloning or pulling the repo if you want to run the examples. -- **livekit-cli** install livekit-cli by following the (official livekit docs)[https://docs.livekit.io/intro/basics/cli/start/] -- **livekit-server** install livekit-server by following the (official livekit docs)[https://docs.livekit.io/transport/self-hosting/local/] +- **livekit-cli** install livekit-cli by following the [official livekit docs](https://docs.livekit.io/intro/basics/cli/start/) +- **livekit-server** install livekit-server by following the [official livekit docs](https://docs.livekit.io/transport/self-hosting/local/) **Platform-Specific Requirements:** diff --git a/tsan_suppressions.txt b/tsan_suppressions.txt new file mode 100644 index 0000000..e12b2b5 --- /dev/null +++ b/tsan_suppressions.txt @@ -0,0 +1,16 @@ +# ThreadSanitizer suppressions for livekit client-sdk-cpp +# +# Rust FFI layer may trigger false positives in TSan because +# TSan cannot see synchronization inside the Rust runtime. +# Add specific suppressions here as needed. +# +# Syntax: +# race: +# deadlock: +# mutex: + +# Rust standard library internals +race:libstd-*.so + +# Rust FFI bridge (livekit_ffi) +race:livekit_ffi_*