Skip to content

Commit ca97f46

Browse files
authored
Merge pull request #289 from fdcastel/fix-issue-288
Add ASAN and Valgrind integration for CI test suite
2 parents d4f4879 + 98115a1 commit ca97f46

File tree

7 files changed

+184
-22
lines changed

7 files changed

+184
-22
lines changed

.github/workflows/build-and-test.yml

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ jobs:
1919
fail-fast: false
2020
matrix:
2121
include:
22-
# ── Windows x64 native ─────────────────────────────────────────────
22+
# ── Windows x64 native ─────────────────────────────────────────────
2323
- os: windows-2022
2424
artifact-name: windows-x64-binaries
2525
firebird-version: '5.0.3'
2626
- os: windows-2022
2727
firebird-branch: master
2828

29-
# ── Windows x86 native (WoW64) ─────────────────────────────────────
29+
# ── Windows x86 native (WoW64) ─────────────────────────────────────
3030
- os: windows-2022
3131
artifact-name: windows-x86-binaries
3232
arch: Win32
@@ -35,26 +35,38 @@ jobs:
3535
arch: Win32
3636
firebird-branch: master
3737

38-
# ── Windows ARM64 native ───────────────────────────────────────────
38+
# ── Windows ARM64 native ─────────────────────────────────────────────
3939
# Official Firebird releases have no win-arm64 binaries; snapshots do.
4040
- os: windows-11-arm
4141
artifact-name: windows-arm64-binaries
4242
firebird-branch: master
4343

44-
# ── Linux x64 native ───────────────────────────────────────────────
44+
# ── Linux x64 native ─────────────────────────────────────────────────
4545
- os: ubuntu-22.04
4646
artifact-name: linux-x64-binaries
4747
firebird-version: '5.0.3'
4848
- os: ubuntu-22.04
4949
firebird-branch: master
5050

51-
# ── Linux ARM64 native ─────────────────────────────────────────────
51+
# ── Linux ARM64 native ─────────────────────────────────────────────
5252
- os: ubuntu-22.04-arm
5353
artifact-name: linux-arm64-binaries
5454
firebird-version: '5.0.3'
5555
- os: ubuntu-22.04-arm
5656
firebird-branch: master
5757

58+
# ── Linux x64 sanitizers (Debug, Firebird 5.0.3) ─────────────────────
59+
# ASAN is temporarily disabled — re-enable once the Linux widechar
60+
# conversion paths in MainUnicode.cpp are rewritten (#287 Tier 1b,
61+
# draft PR #291). Until then it fails on a heap-buffer-overflow in
62+
# SQLGetDiagRecW.
63+
# - os: ubuntu-22.04
64+
# sanitizer: Asan
65+
# firebird-version: '5.0.3'
66+
- os: ubuntu-22.04
67+
sanitizer: Valgrind
68+
firebird-version: '5.0.3'
69+
5870
runs-on: ${{ matrix.os }}
5971

6072
steps:
@@ -70,6 +82,10 @@ jobs:
7082
if: runner.os == 'Linux'
7183
run: sudo apt-get update && sudo apt-get install -y unixodbc unixodbc-dev
7284

85+
- name: Install Valgrind
86+
if: matrix.sanitizer == 'Valgrind'
87+
run: sudo apt-get install -y valgrind
88+
7389
- name: Build, install and test
7490
shell: pwsh
7591
env:
@@ -79,7 +95,10 @@ jobs:
7995
run: |
8096
$archArgs = @{}
8197
if ('${{ matrix.arch }}') { $archArgs['Architecture'] = '${{ matrix.arch }}' }
82-
Invoke-Build test -Configuration Release @archArgs -File ./firebird-odbc-driver.build.ps1
98+
$sanitizerArgs = @{}
99+
if ('${{ matrix.sanitizer }}') { $sanitizerArgs['Sanitizer'] = '${{ matrix.sanitizer }}' }
100+
$config = if ('${{ matrix.sanitizer }}') { 'Debug' } else { 'Release' }
101+
Invoke-Build test -Configuration $config @archArgs @sanitizerArgs -File ./firebird-odbc-driver.build.ps1
83102
84103
- name: Upload artifacts (Windows)
85104
if: runner.os == 'Windows' && matrix.artifact-name

CMakeLists.txt

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,41 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
4141
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
4242
option(BUILD_TESTING "Build tests" ON)
4343

44+
# ---------------------------------------------------------------------------
45+
# Sanitizer options
46+
# ---------------------------------------------------------------------------
47+
option(BUILD_WITH_ASAN "Enable AddressSanitizer (-fsanitize=address)" OFF)
48+
option(BUILD_WITH_VALGRIND "Enable Valgrind memcheck via CTest" OFF)
49+
50+
if(BUILD_WITH_ASAN AND BUILD_WITH_VALGRIND)
51+
message(FATAL_ERROR "BUILD_WITH_ASAN and BUILD_WITH_VALGRIND are mutually exclusive. "
52+
"ASAN instruments the binary at compile time; Valgrind instruments at runtime. "
53+
"They must not be combined.")
54+
endif()
55+
56+
if(MSVC AND (BUILD_WITH_ASAN OR BUILD_WITH_VALGRIND))
57+
message(WARNING "Sanitizers are not yet supported on MSVC. "
58+
"BUILD_WITH_ASAN / BUILD_WITH_VALGRIND will be ignored.")
59+
elseif(NOT MSVC)
60+
if(BUILD_WITH_ASAN)
61+
message(STATUS "AddressSanitizer: ENABLED")
62+
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
63+
add_link_options(-fsanitize=address)
64+
endif()
65+
66+
if(BUILD_WITH_VALGRIND)
67+
find_program(VALGRIND_COMMAND valgrind)
68+
if(NOT VALGRIND_COMMAND)
69+
message(FATAL_ERROR "Valgrind not found but BUILD_WITH_VALGRIND is ON. "
70+
"Install it with: sudo apt-get install valgrind")
71+
endif()
72+
message(STATUS "Valgrind memcheck: ENABLED (${VALGRIND_COMMAND})")
73+
set(MEMORYCHECK_COMMAND ${VALGRIND_COMMAND})
74+
set(MEMORYCHECK_COMMAND_OPTIONS
75+
"--leak-check=full --error-exitcode=1 --suppressions=${CMAKE_SOURCE_DIR}/valgrind.supp")
76+
endif()
77+
endif()
78+
4479
# ---------------------------------------------------------------------------
4580
# CPU architecture detection (from PR #248)
4681
# ---------------------------------------------------------------------------
@@ -95,12 +130,19 @@ else()
95130

96131
# Compiler optimization flags (from PR #248 / old makefile.linux)
97132
add_compile_options(
98-
"$<$<CONFIG:Debug>:-O0;-g3;-D_DEBUG;-DDEBUG;-DLOGGING;-fexceptions>"
133+
"$<$<CONFIG:Debug>:-O0;-g3;-D_DEBUG;-fexceptions>"
99134
"$<$<CONFIG:Release>:-O3;-DNDEBUG;-ftree-loop-vectorize>"
100135
"$<$<CONFIG:RelWithDebInfo>:-O2;-g;-DNDEBUG>"
101136
"$<$<CONFIG:MinSizeRel>:-Os;-DNDEBUG>"
102137
)
103138

139+
# Debug macros: DEBUG and LOGGING are for Debug builds only;
140+
# sanitizer builds strip them for cleaner output (less noise in reports).
141+
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT BUILD_WITH_ASAN AND NOT BUILD_WITH_VALGRIND)
142+
add_definitions(-DDEBUG)
143+
add_definitions(-DLOGGING)
144+
endif()
145+
104146
# SSE4.1 for x86/x86_64 (from PR #248)
105147
if(FBODBC_ARCH STREQUAL "x86" OR FBODBC_ARCH STREQUAL "i686"
106148
OR FBODBC_ARCH STREQUAL "x86_64" OR FBODBC_ARCH STREQUAL "AMD64")
@@ -246,12 +288,6 @@ else()
246288
)
247289
endif()
248290

249-
# Debug-specific definitions (matching .vcxproj: DEBUG;LOGGING for Debug configs)
250-
target_compile_definitions(OdbcFb PRIVATE
251-
$<$<CONFIG:Debug>:DEBUG>
252-
$<$<CONFIG:Debug>:LOGGING>
253-
)
254-
255291
# ---------------------------------------------------------------------------
256292
# Testing
257293
# ---------------------------------------------------------------------------

CMakePresets.json

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,47 @@
1212
"description": "Default preset — uses 'build/' as the binary directory so that all tools (command-line, Visual Studio, CLion, VS Code) share the same layout.",
1313
"binaryDir": "${sourceDir}/build",
1414
"cacheVariables": {
15-
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install"
15+
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install",
16+
"BUILD_WITH_ASAN": "OFF",
17+
"BUILD_WITH_VALGRIND": "OFF"
1618
}
1719
},
1820
{
1921
"name": "release",
2022
"displayName": "Release",
2123
"inherits": "default",
2224
"cacheVariables": {
23-
"CMAKE_BUILD_TYPE": "Release"
25+
"CMAKE_BUILD_TYPE": "Release",
26+
"BUILD_WITH_ASAN": "OFF",
27+
"BUILD_WITH_VALGRIND": "OFF"
2428
}
2529
},
2630
{
2731
"name": "debug",
2832
"displayName": "Debug",
2933
"inherits": "default",
3034
"cacheVariables": {
31-
"CMAKE_BUILD_TYPE": "Debug"
35+
"CMAKE_BUILD_TYPE": "Debug",
36+
"BUILD_WITH_ASAN": "OFF",
37+
"BUILD_WITH_VALGRIND": "OFF"
38+
}
39+
},
40+
{
41+
"name": "asan",
42+
"displayName": "Debug + AddressSanitizer",
43+
"inherits": "debug",
44+
"cacheVariables": {
45+
"BUILD_WITH_ASAN": "ON",
46+
"BUILD_WITH_VALGRIND": "OFF"
47+
}
48+
},
49+
{
50+
"name": "valgrind",
51+
"displayName": "Debug + Valgrind",
52+
"inherits": "debug",
53+
"cacheVariables": {
54+
"BUILD_WITH_VALGRIND": "ON",
55+
"BUILD_WITH_ASAN": "OFF"
3256
}
3357
}
3458
],
@@ -42,6 +66,16 @@
4266
"name": "debug",
4367
"configurePreset": "default",
4468
"configuration": "Debug"
69+
},
70+
{
71+
"name": "asan",
72+
"configurePreset": "asan",
73+
"configuration": "Debug"
74+
},
75+
{
76+
"name": "valgrind",
77+
"configurePreset": "valgrind",
78+
"configuration": "Debug"
4579
}
4680
],
4781
"testPresets": [
@@ -51,6 +85,26 @@
5185
"output": {
5286
"outputOnFailure": true
5387
}
88+
},
89+
{
90+
"name": "asan",
91+
"configurePreset": "asan",
92+
"output": {
93+
"outputOnFailure": true
94+
},
95+
"environment": {
96+
"ASAN_OPTIONS": "detect_leaks=1:halt_on_error=1:print_stats=1"
97+
}
98+
},
99+
{
100+
"name": "valgrind",
101+
"configurePreset": "valgrind",
102+
"output": {
103+
"outputOnFailure": true
104+
},
105+
"execution": {
106+
"timeout": 600
107+
}
54108
}
55109
]
56110
}

cmake/GetVersionFromGit.cmake

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ if(NOT GIT_FOUND)
2222
return()
2323
endif()
2424

25-
# Try to find the latest semver tag matching v<MAJOR>.<MINOR>.<PATCH>
26-
# We use --match to only consider tags in vX.Y.Z format (not rc, temp, etc.)
25+
# Find the latest semver tag matching v<MAJOR>.<MINOR>.<PATCH>.
26+
# Prefer a stable vX.Y.Z tag (--exclude "*-*" filters out prereleases like
27+
# v3.5.0-rc1). If no stable tag exists yet, fall back to any matching tag;
28+
# the parser regex below handles the optional -<prerelease> suffix.
29+
# NOTE: --always is intentionally NOT used on the first call so that a
30+
# "no stable tag" case fails with non-zero exit and triggers the fallback.
2731
execute_process(
2832
COMMAND ${GIT_EXECUTABLE} describe --tags --match "v[0-9]*.[0-9]*.[0-9]*"
29-
--exclude "*-*" --long --always
33+
--exclude "*-*" --long
3034
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
3135
OUTPUT_VARIABLE GIT_DESCRIBE_OUTPUT
3236
OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -35,10 +39,9 @@ execute_process(
3539
)
3640

3741
if(NOT GIT_DESCRIBE_RESULT EQUAL 0)
38-
# No matching tag found at all, try without --exclude
3942
execute_process(
4043
COMMAND ${GIT_EXECUTABLE} describe --tags --match "v[0-9]*.[0-9]*.[0-9]*"
41-
--long --always
44+
--long
4245
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
4346
OUTPUT_VARIABLE GIT_DESCRIBE_OUTPUT
4447
OUTPUT_STRIP_TRAILING_WHITESPACE

firebird-odbc-driver.build.ps1

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ param(
1919
[string]$Configuration = 'Debug',
2020

2121
[ValidateSet('', 'Win32')]
22-
[string]$Architecture = ''
22+
[string]$Architecture = '',
23+
24+
[ValidateSet('None', 'Asan', 'Valgrind')]
25+
[string]$Sanitizer = 'None'
2326
)
2427

2528
# Detect OS
@@ -66,6 +69,16 @@ task build {
6669
if ($IsWindowsOS -and $Architecture) {
6770
$cmakeArgs += @('-A', $Architecture)
6871
}
72+
if ($Sanitizer -ne 'None') {
73+
if ($IsWindowsOS) {
74+
print Yellow "WARNING: Sanitizer=$Sanitizer is not supported on Windows. Building without sanitizer."
75+
} else {
76+
switch ($Sanitizer) {
77+
'Asan' { $cmakeArgs += '-DBUILD_WITH_ASAN=ON' }
78+
'Valgrind' { $cmakeArgs += '-DBUILD_WITH_VALGRIND=ON' }
79+
}
80+
}
81+
}
6982
exec { cmake @cmakeArgs }
7083

7184
if ($IsWindowsOS) {
@@ -160,6 +173,13 @@ task test build, build-test-databases, install, {
160173
print Yellow 'WARNING: FIREBIRD_ODBC_CONNECTION environment variable is not set. Using built-in connection strings.'
161174
}
162175

176+
# Set sanitizer runtime options
177+
if ($Sanitizer -eq 'Asan' -and -not $IsWindowsOS) {
178+
$env:ASAN_OPTIONS = 'detect_leaks=1:halt_on_error=1:print_stats=1'
179+
$env:LSAN_OPTIONS = "suppressions=$(Join-Path $BuildRoot 'lsan.supp')"
180+
print Cyan "ASAN_OPTIONS=$env:ASAN_OPTIONS"
181+
}
182+
163183
# Test suites that exercise charset/encoding-sensitive code paths.
164184
$charsetSensitiveSuites = @(
165185
'WCharTest'

lsan.supp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# LeakSanitizer suppressions for Firebird ODBC Driver test suite
2+
#
3+
# This file suppresses known leak reports from third-party libraries
4+
# (Firebird client, unixODBC, system allocators, etc.).
5+
# Populate as false positives are discovered during ASAN/LSAN runs.
6+
#
7+
# Usage:
8+
# export LSAN_OPTIONS="suppressions=lsan.supp"
9+
10+
leak:libfbclient
11+
leak:libodbc
12+
leak:libodbcinst

valgrind.supp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Valgrind suppressions for Firebird ODBC Driver test suite
2+
#
3+
# This file suppresses known false positives from third-party libraries
4+
# (Firebird client, unixODBC, system allocators, etc.).
5+
# Populate as false positives are discovered during Valgrind runs.
6+
#
7+
# Usage:
8+
# valgrind --leak-check=full --suppressions=valgrind.supp ./firebird_odbc_tests
9+
#
10+
# Or via CTest (when ENABLE_VALGRIND=ON):
11+
# ctest -T memcheck
12+
13+
{
14+
fbclient_global_init
15+
Memcheck:Leak
16+
...
17+
obj:*/libfbclient.so*
18+
}

0 commit comments

Comments
 (0)