Skip to content

Commit b729f01

Browse files
olupton1ucnrnhines
authored
ThreadSanitizer fixes and CI (neuronsimulator#2034)
* Update submodule with TSan + GCC support. * Add Ubuntu 22.04 + TSan + GCC CI job. * Fix some issues identified by TSan. * Fix automatic formatting of MUTDEC . Co-authored-by: Luc Grosheintz <[email protected]> Co-authored-by: nrnhines <[email protected]>
1 parent aaa5967 commit b729f01

28 files changed

+678
-352
lines changed

.clang-format.changes

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
SortIncludes: false
22
Standard: c++17
3-
StatementMacros: [MKDLL, MKDLLdec, MKDLLif, MKDLLvp, MKDLLvpf, PyObject_HEAD,
3+
StatementMacros: [MKDLL, MKDLLdec, MKDLLif, MKDLLvp, MKDLLvpf, MUTDEC, PyObject_HEAD,
44
declareActionCallback, declareAdjustStepper, declareArrowGlyph, declareFieldEditorCallback,
55
declareFieldSEditorCallback, declareFileChooserCallback, declareIOCallback, declareList,
66
declarePtrList, declareRubberCallback, declareSelectionCallback, declareTable, declareTable2,

.github/problem-matchers/thread.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
{
3+
"problemMatcher": [
4+
{
5+
"owner": "tsan-problem-matcher",
6+
"severity": "warning",
7+
"pattern": [
8+
{
9+
"regexp": "^.*ThreadSanitizer: (.*)$",
10+
"message": 1
11+
}
12+
]
13+
}
14+
]
15+
}

.github/workflows/neuron-ci.yml

+24-9
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,34 @@ jobs:
5555
- os: ubuntu-22.04
5656
config:
5757
build_mode: cmake
58-
# TODO: -DNMODL_SANITIZERS=undefined
5958
cmake_option: -DNRN_ENABLE_CORENEURON=ON
60-
-DNRN_ENABLE_INTERVIEWS=OFF
59+
-DNRN_ENABLE_INTERVIEWS=OFF -DNMODL_SANITIZERS=undefined
6160
flag_warnings: ON
6261
sanitizer: undefined
6362
- os: ubuntu-22.04
6463
config:
6564
build_mode: cmake
66-
# TODO: -DNMODL_SANITIZERS=address,leak
6765
# TODO: CoreNEURON is only LeakSanitizer-clean if we disable MPI
6866
cmake_option: -DNRN_ENABLE_CORENEURON=ON
69-
-DNRN_ENABLE_INTERVIEWS=OFF
67+
-DNRN_ENABLE_INTERVIEWS=OFF -DNMODL_SANITIZERS=address
7068
# TODO: address-leak is the dream, but there are many problems,
7169
# including external ones from the MPI implementations
7270
sanitizer: address
71+
- os: ubuntu-22.04
72+
config:
73+
build_mode: cmake
74+
# Cannot use a non-instrumented OpenMP with TSan, and we don't
75+
# have a TSan-instrumented OpenMP runtime available.
76+
# TODO: debug RX3D + TSan
77+
cmake_option: -DNRN_ENABLE_CORENEURON=ON -DNRN_ENABLE_MPI=OFF
78+
-DCORENRN_ENABLE_OPENMP=OFF -DNRN_ENABLE_RX3D=OFF
79+
sanitizer: thread
7380
- os: macOS-12
7481
config:
7582
build_mode: cmake
7683
# TODO: investigate rxd test timeouts in this build and re-enable them
7784
cmake_option: -DNRN_ENABLE_CORENEURON=ON -DNRN_ENABLE_INTERVIEWS=OFF
78-
-DNRN_ENABLE_RX3D=OFF
85+
-DNRN_ENABLE_RX3D=OFF -DNMODL_SANITIZERS=address
7986
sanitizer: address
8087
fail-fast: false
8188

@@ -252,10 +259,18 @@ jobs:
252259
# Sanitizer-specific setup
253260
if [[ -n "${{matrix.config.sanitizer}}" ]]; then
254261
if [ "$RUNNER_OS" == "Linux" ]; then
255-
CC=$(command -v clang-14)
256-
CXX=$(command -v clang++-14)
257-
symbolizer_path=$(realpath $(command -v llvm-symbolizer-14))
258-
cmake_args+=(-DLLVM_SYMBOLIZER_PATH="${symbolizer_path}")
262+
if [[ "${{matrix.config.sanitizer}}" == "thread" ]]; then
263+
# GitHub/ubuntu-22.04 + clang-14 seems to have problems with TSan.
264+
# Vanilla 22.04 + clang-16 from apt.llvm.org seemed to work.
265+
# Use gcc-12 instead, as GitHub/ubuntu-22.04 already has it.
266+
CC=$(command -v gcc-12)
267+
CXX=$(command -v g++-12)
268+
else
269+
CC=$(command -v clang-14)
270+
CXX=$(command -v clang++-14)
271+
symbolizer_path=$(realpath $(command -v llvm-symbolizer-14))
272+
cmake_args+=(-DLLVM_SYMBOLIZER_PATH="${symbolizer_path}")
273+
fi
259274
fi
260275
cmake_args+=(-DCMAKE_BUILD_TYPE=Custom \
261276
-DCMAKE_C_FLAGS="-O1 -g" \

.sanitizers/thread.supp

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
deadlock:mca_btl_tcp_add_procs
2+
deadlock:mca_btl_tcp_proc_remove
3+
race:pmix_ptl_base_start_listening
4+
race:pmix_ptl_base_stop_listening

cmake/SanitizerHelper.cmake

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ if(NRN_SANITIZERS)
3333
if("address" IN_LIST nrn_sanitizers)
3434
list(APPEND NRN_COMPILE_DEFS NRN_ASAN_ENABLED)
3535
endif()
36+
if("thread" IN_LIST nrn_sanitizers)
37+
list(APPEND NRN_COMPILE_DEFS NRN_TSAN_ENABLED)
38+
endif()
3639
# generate and install a launcher script called nrn-enable-sanitizer [--preload] that sets
3740
# *SAN_OPTIONS variables and, optionally, LD_PRELOAD -- this is useful both in CI configuration
3841
# and when using the sanitizers "downstream" of NEURON

docs/cmake_doc/options.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -521,10 +521,10 @@ NRN_COVERAGE_FILES:STRING=
521521

522522
NRN_SANITIZERS:STRING=
523523
----------------------
524-
Enable some combination of AddressSanitizer, LeakSanitizer and
525-
UndefinedBehaviorSanitizer. Accepts a comma-separated list of ``address``,
526-
``leak`` and ``undefined``. See the "Diagnosis and Debugging" section for more
527-
information.
524+
Enable some combination of AddressSanitizer, LeakSanitizer, ThreadSanitizer
525+
and UndefinedBehaviorSanitizer. Accepts a comma-separated list of ``address``,
526+
``leak``, ``thread`` and ``undefined``.
527+
See the "Diagnosis and Debugging" section for more information.
528528
Note that on macOS it can be a little intricate to combine
529529
``-DNRN_SANITIZERS=address`` with the use of Python virtual environments; if
530530
you attempt this then the CMake code should recommend a solution.

docs/dev/data-structures.rst

+2-3
Original file line numberDiff line numberDiff line change
@@ -496,9 +496,8 @@ This is done using a token type:
496496

497497
void whatever(my_soa_container& data) { // assume `data` was not already "frozen"
498498
owning_handle foo{data}; // adding an element is OK
499-
// in reality you would probably apply a permutation here before marking data sorted
500-
auto token = data.get_sorted_token(); // mark container "frozen" so it remains sorted
501-
owning_handle disappointing_foo{data}; // this will throw
499+
auto token = data.issue_frozen_token(); // mark container "frozen"
500+
owning_handle disappointing_foo{data}; // this will throw
502501
} // `token`` is destroyed here; `data` ceases to be "frozen"
503502

504503
The container maintains a count of how many tokens are controlling it and is "frozen" whenever that

docs/install/debug.md

+25-53
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ the next valgrind error.
128128

129129

130130
#### Sanitizers
131-
The `AddressSanitizer` (ASan), `LeakSanitizer` (LSan), `ThreadSanitizer` (TSan,
132-
see below) and `UndefinedBehaviorSanitizer` (UBSan) are a collection of tools
131+
The `AddressSanitizer` (ASan), `LeakSanitizer` (LSan), `ThreadSanitizer` (TSan)
132+
and `UndefinedBehaviorSanitizer` (UBSan) are a collection of tools
133133
that rely on compiler instrumentation to catch dangerous behaviour at runtime.
134134
Compiler support is widespread but not ubiquitous, but both Clang and GCC
135135
provide support.
@@ -147,13 +147,14 @@ The typical example of this case is loading NEURON from Python, where the
147147
As of [#1842](https://github.com/neuronsimulator/nrn/pull/1842), the NEURON
148148
build system is aware of ASan, LSan and UBSan, and it tries to configure the
149149
sanitizers correctly based on the `NRN_SANITIZERS` CMake variable.
150+
In [#2034](https://github.com/neuronsimulator/nrn/pull/2034) this was extended
151+
to TSan via `-DNRN_SANITIZERS=thread`, and support for GCC was added.
150152
For example, `cmake -DNRN_SANITIZERS=address,leak ...` will enable ASan and
151153
LSan, while `-DNRN_SANITIZERS=undefined` will enable UBSan.
152-
Not all combinations of sanitizers are possible, so far ASan, ASan+LSan and
153-
UBSan have been tested with Clang, GCC has not been tested and likely needs
154-
minor changes.
155-
Support for standalone LSan and for TSan (see below for manual instructions)
156-
should be possible without major difficulties, but is not yet implemented.
154+
Not all combinations of sanitizers are possible, so far ASan, ASan+LSan, TSan
155+
and UBSan have been tested with Clang.
156+
Support for standalone LSan should be possible without major difficulties, but
157+
is not yet implemented.
157158

158159
Depending on your system, you may also need to set the `LLVM_SYMBOLIZER_PATH`
159160
variable to point to the `llvm-symbolizer` executable matching your Clang.
@@ -176,59 +177,30 @@ use `nrn-enable-sanitizer special -python path/to/script.py` or
176177
because the `python` binary is (presumably) not linked against the sanitizer
177178
runtime library.
178179

179-
LSan and UBSan support suppression files, which can be used to prevent tests
180-
failing due to known issues.
181-
NEURON includes a suppression file for UBSan under `.sanitizers/undefined.supp`
182-
in the GitHub repository, no LSan equivalent exists for the moment.
180+
LSan, TSan and UBSan support suppression files, which can be used to prevent
181+
tests failing due to known issues.
182+
NEURON includes a suppression file for TSan under `.sanitizers/thread.supp` and
183+
one for UBSan under `.sanitizers/undefined.supp` in the GitHub repository, no
184+
LSan equivalent exists for the moment.
183185

184186
Note that LSan and MPI implementations typically do not play nicely together, so
185187
if you want to use LSan with NEURON, you may need to disable MPI or add a
186188
suppression file that is tuned to your MPI implementation.
187189

188-
The GitHub Actions CI for NEURON at the time of writing includes test jobs for
189-
ASan and UBSan using Clang 14, but does not enable LSan.
190+
Similarly, TSan does not work very well with MPI and (especially) OpenMP
191+
implementations that were not compiled with TSan instrumentation (which they
192+
are typically not).
190193

191-
CoreNEURON and NMODL both support the sanitizers in a similar way, but this has
192-
to be enabled explicitly: `-DNRN_SANITIZERS=undefined` will not compile
193-
CoreNEURON code with UBSan enabled, you must additionally pass
194-
`-DCORENRN_SANITIZERS=undefined` to enable instrumentation of CoreNEURON code.
195-
The equivalent variable for NMODL is `NMODL_SANITIZERS`.
196-
197-
#### ThreadSanitizer (TSAN)
198-
`ThreadSanitizer` is a tool that detects data races. Be aware that a slowdown is incurred by using ThreadSanitizer of about 5x-15x, with typical memory overhead of about 5x-10x.
199-
200-
Here is how to enable it:
201-
```
202-
cmake ... -DNRN_ENABLE_TESTS=ON -DCMAKE_C_FLAGS="-O0 -fno-inline -g -fsanitize=thread" -DCMAKE_CXX_FLAGS="-O0 -fno-inline -g -fsanitize=thread" ..
203-
```
204-
You can then target a specific test (for example `ctest -VV -R test_name` or `bin/nrniv -nogui -nopython test.hoc`) and have a look at the generated output. In case of data races, you would see something similar to:
205-
```
206-
94: WARNING: ThreadSanitizer: data race (pid=2572)
207-
94: Read of size 8 at 0x7b3c00000bf0 by thread T1:
208-
94: #0 Cvode::at_time(double, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/cvodeobj.cpp:751 (libnrniv.so+0x38673e)
209-
94: #1 at_time /home/savulesc/Workspace/nrn/src/nrncvode/cvodestb.cpp:133 (libnrniv.so+0x389e27)
210-
94: #2 _nrn_current__IClamp /home/savulesc/Workspace/nrn/src/nrnoc/stim.c:266 (libnrniv.so+0x5b8f02)
211-
94: #3 _nrn_cur__IClamp /home/savulesc/Workspace/nrn/src/nrnoc/stim.c:306 (libnrniv.so+0x5b9236)
212-
94: #4 Cvode::rhs_memb(CvMembList*, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/cvtrset.cpp:68 (libnrniv.so+0x38a0eb)
213-
94: #5 Cvode::rhs(NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/cvtrset.cpp:35 (libnrniv.so+0x38a2f6)
214-
94: #6 Cvode::fun_thread_transfer_part2(double*, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/occvode.cpp:671 (libnrniv.so+0x3bbbf1)
215-
94: #7 Cvode::fun_thread(double, double*, double*, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/occvode.cpp:639 (libnrniv.so+0x3bd049)
216-
94: #8 f_thread /home/savulesc/Workspace/nrn/src/nrncvode/cvodeobj.cpp:1532 (libnrniv.so+0x384f45)
217-
94: #9 slave_main /home/savulesc/Workspace/nrn/src/nrnoc/multicore.cpp:337 (libnrniv.so+0x5157ee)
218-
94:
219-
94: Previous write of size 8 at 0x7b3c00000bf0 by main thread:
220-
94: #0 Cvode::at_time(double, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/cvodeobj.cpp:753 (libnrniv.so+0x386759)
221-
94: #1 at_time /home/savulesc/Workspace/nrn/src/nrncvode/cvodestb.cpp:133 (libnrniv.so+0x389e27)
222-
94: #2 _nrn_current__IClamp /home/savulesc/Workspace/nrn/src/nrnoc/stim.c:266 (libnrniv.so+0x5b8f02)
223-
94: #3 _nrn_cur__IClamp /home/savulesc/Workspace/nrn/src/nrnoc/stim.c:306 (libnrniv.so+0x5b9236)
224-
94: #4 Cvode::rhs_memb(CvMembList*, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/cvtrset.cpp:68 (libnrniv.so+0x38a0eb)
225-
94: #5 Cvode::rhs(NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/cvtrset.cpp:35 (libnrniv.so+0x38a2f6)
226-
94: #6 Cvode::fun_thread_transfer_part2(double*, NrnThread*) /home/savulesc/Workspace/nrn/src/nrncvode/occvode.cpp:671 (libnrniv.so+0x3bbbf1)
227-
..............................................................
228-
94: SUMMARY: ThreadSanitizer: data race /home/savulesc/Workspace/nrn/src/nrncvode/cvodeobj.cpp:751 in Cvode::at_time(double, NrnThread*)
229-
94: ==================
230-
```
194+
The GitHub Actions CI for NEURON at the time of writing includes three jobs
195+
using Ubuntu 22.04: ASan (but not LSan) using Clang 14, UBSan using Clang 14,
196+
and TSan using GCC 12.
197+
In addition, there is a macOS-based ASan build using AppleClang, which has the
198+
advantage that it uses `libc++` instead of `libstdc++`.
231199

200+
NMODL supports the sanitizers in a similar way, but this has to be enabled
201+
explicitly: `-DNRN_SANITIZERS=undefined` will not compile NMODL code with UBSan
202+
enabled, you must additionally pass `-DNMODL_SANITIZERS=undefined` to enable
203+
instrumentation of NMODL code.
232204

233205
Profiling and performance benchmarking
234206
--------------------------------------

src/coreneuron/io/nrn_setup.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,7 @@ void nrn_setup(const char* filesdat,
478478
} else {
479479
nrn_multithread_job([](NrnThread* n) {
480480
Phase1 p1{n->id};
481-
NrnThread& nt = *n;
482-
p1.populate(nt, mut);
481+
p1.populate(*n, mut);
483482
});
484483
}
485484

0 commit comments

Comments
 (0)