Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions .github/workflows/builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,103 @@ jobs:
./build.sh clean-all || true
fi

sanitizers:
strategy:
fail-fast: false
matrix:
include:
- name: asan
preset: linux-debug-asan
build_dir: build-debug-asan
env_vars: ""
- name: tsan
preset: linux-debug-tsan
build_dir: build-debug-tsan
env_vars: "TSAN_OPTIONS=suppressions=$GITHUB_WORKSPACE/tsan_suppressions.txt:halt_on_error=1"

name: Sanitizer (${{ matrix.name }})
runs-on: ubuntu-latest

steps:
- name: Checkout (with submodules)
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
submodules: recursive
fetch-depth: 1

- name: Pull LFS files
run: git lfs pull

- name: Install deps (Ubuntu)
run: |
set -eux
sudo apt-get update
sudo apt-get install -y \
build-essential cmake ninja-build pkg-config \
llvm-dev libclang-dev clang \
libva-dev libdrm-dev libgbm-dev libx11-dev libgl1-mesa-dev \
libxext-dev libxcomposite-dev libxdamage-dev libxfixes-dev \
libxrandr-dev libxi-dev libxkbcommon-dev \
libasound2-dev libpulse-dev \
libssl-dev \
libprotobuf-dev protobuf-compiler \
libabsl-dev \
libwayland-dev libdecor-0-dev \
libspdlog-dev

- name: Install Rust (stable)
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
with:
toolchain: stable

- name: Cache Cargo registry
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
key: Linux-sanitizer-${{ matrix.name }}-cargo-reg-${{ hashFiles('**/Cargo.lock') }}
restore-keys: Linux-sanitizer-${{ matrix.name }}-cargo-reg-

- name: Cache Cargo target
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: client-sdk-rust/target
key: Linux-sanitizer-${{ matrix.name }}-cargo-target-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
Linux-sanitizer-${{ matrix.name }}-cargo-target-

- name: Set build environment
run: |
echo "CXXFLAGS=-Wno-deprecated-declarations" >> $GITHUB_ENV
echo "CFLAGS=-Wno-deprecated-declarations" >> $GITHUB_ENV
LLVM_VERSION=$(llvm-config --version | cut -d. -f1)
echo "LIBCLANG_PATH=/usr/lib/llvm-${LLVM_VERSION}/lib" >> $GITHUB_ENV

- name: Configure
run: cmake --preset ${{ matrix.preset }}

- name: Build
run: cmake --build --preset ${{ matrix.preset }}

- name: Run unit tests
Copy link
Copy Markdown
Contributor

@alan-george-lk alan-george-lk Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there @siddimore: can you explain the decision to make this a separate stage (Sanitizer - <arch>) vs. incorporating the sanitizer steps into the existing build steps?

There's a lot of duplication in setup here, and definitely a tradeoff between parallelizing the workflow vs. too much duplication.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a fair point since this was my first PR in this repo wanted to tread carefully plus these other reasons:

  • ASan/TSan require recompilation with different flags (-fsanitize=address vs -fsanitize=thread), so you can't just re-run existing binaries
  • Existing matrix includes macOS and Windows, which don't need sanitizer runs
  • Isolation — a TSan failure shouldn't block the macOS or Windows build result

but i m happy to avoid the duplication which u identified in the setup which is the right call. I will update the PR later today. Thanks for your review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello again, I actually thought more about your PR after my original comment, and for sure the different compiler flags make a difference. For context, I am working on a "quality build" stage that ideally could handle the following:

  • clang-tidy (open PR)
  • sanitizer builds
  • code coverage (debug flag builds)

It will probably take a little bit more time for me to fully flesh out that idea and see what stages we could combine configuration/compiler-flag wise. So feel free to iterate on your PR here, but we will probably want to bundle that in later with the other quality items. Stay tuned, thanks!

run: |
${{ matrix.env_vars }} \
${{ matrix.build_dir }}/bin/livekit_unit_tests \
--gtest_output=xml:${{ matrix.build_dir }}/unit-test-results.xml

- name: Upload test results
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: test-results-sanitizer-${{ matrix.name }}
path: ${{ matrix.build_dir }}/unit-test-results.xml
retention-days: 7

- name: Cleanup
if: always()
run: rm -rf ${{ matrix.build_dir }} || true

docker-build-x64:
name: Build (docker-linux-x64)
runs-on: ubuntu-latest
Expand Down
24 changes: 24 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
48 changes: 48 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down Expand Up @@ -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": [
Expand Down Expand Up @@ -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
}
}
]
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**

Expand Down
16 changes: 16 additions & 0 deletions tsan_suppressions.txt
Original file line number Diff line number Diff line change
@@ -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:<function_or_source_pattern>
# deadlock:<function_or_source_pattern>
# mutex:<function_or_source_pattern>

# Rust standard library internals
race:libstd-*.so

# Rust FFI bridge (livekit_ffi)
race:livekit_ffi_*
Loading