Skip to content

Commit 0f8d5f2

Browse files
YannickJadoulbstaletichenryiii
authored
Add a Valgrind build on debug Python 3.9 (#2746)
* Adding a valgrind build on debug Python 3.9 Co-authored-by: Boris Staletic <[email protected]> * Add Valgrind suppression files - Introduce suppression file, populate it with a first suppression taken from CPython, and fix one leak in the tests - Suppress leak in NumPy - More clean tests! - Tests with names a-e passing (except for test_buffer) - Suppress multiprocessing errors - Merge multiprocessing suppressions into other suppression files - Numpy seems to be spelled with a big P - Append single entry from valgrind-misc.supp to valgrind-python.supp, and make clear valgrind-python.supp is only CPython Co-authored-by: Boris Staletic <[email protected]> * Enable test_virtual_functions with a workaround * Add a memcheck cmake target - Add a memcheck cmake target - Reformat cmake - Appease the formatting overlords - they are angry - Format CMake valgrind target decently * Update CI config to new action versions * fix: separate memcheck from pytest * ci: cleanup * Merge Valgrind and other deadsnakes builds Co-authored-by: Boris Staletic <[email protected]> Co-authored-by: Henry Schreiner <[email protected]>
1 parent 76a1600 commit 0f8d5f2

File tree

6 files changed

+327
-14
lines changed

6 files changed

+327
-14
lines changed

.github/workflows/ci.yml

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,30 +169,61 @@ jobs:
169169
strategy:
170170
fail-fast: false
171171
matrix:
172-
python:
173-
- version: 3.9
174-
debug: true
175-
- version: 3.10-dev
176-
debug: false
172+
include:
173+
- python-version: 3.9
174+
python-debug: true
175+
valgrind: true
176+
- python-version: 3.10-dev
177+
python-debug: false
177178

178-
name: "🐍 ${{ matrix.python.version }}${{ matrix.python.debug && ' (debug)' || '' }} • deadsnakes • x64"
179+
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
179180
runs-on: ubuntu-latest
180181

181182
steps:
182183
- uses: actions/checkout@v2
183184

184-
- name: Setup Python ${{ matrix.python.version }} (deadsnakes)
185+
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
185186
uses: deadsnakes/[email protected]
186187
with:
187-
python-version: ${{ matrix.python.version }}
188-
debug: ${{ matrix.python.debug }}
188+
python-version: ${{ matrix.python-version }}
189+
debug: ${{ matrix.python-debug }}
190+
191+
- name: Update CMake
192+
uses: jwlawson/[email protected]
193+
194+
- name: Valgrind cache
195+
if: matrix.valgrind
196+
uses: actions/cache@v2
197+
id: cache-valgrind
198+
with:
199+
path: valgrind
200+
key: 3.16.1 # Valgrind version
201+
202+
- name: Compile Valgrind
203+
if: matrix.valgrind && steps.cache-valgrind.outputs.cache-hit != 'true'
204+
run: |
205+
VALGRIND_VERSION=3.16.1
206+
curl https://sourceware.org/pub/valgrind/valgrind-$VALGRIND_VERSION.tar.bz2 -o - | tar xj
207+
mv valgrind-$VALGRIND_VERSION valgrind
208+
cd valgrind
209+
./configure
210+
make -j 2 > /dev/null
211+
212+
- name: Install Valgrind
213+
if: matrix.valgrind
214+
working-directory: valgrind
215+
run: |
216+
sudo make install
217+
sudo apt-get update
218+
sudo apt-get install libc6-dbg # Needed by Valgrind
189219
190220
- name: Prepare env
191221
run: python -m pip install -r tests/requirements.txt --prefer-binary
192222

193223
- name: Configure
194224
run: >
195225
cmake -S . -B build
226+
-DCMAKE_BUILD_TYPE=Debug
196227
-DPYBIND11_WERROR=ON
197228
-DDOWNLOAD_CATCH=ON
198229
-DDOWNLOAD_EIGEN=ON
@@ -207,6 +238,10 @@ jobs:
207238
- name: C++ tests
208239
run: cmake --build build --target cpptest
209240

241+
- name: Run Valgrind on Python tests
242+
if: matrix.valgrind
243+
run: cmake --build build --target memcheck
244+
210245

211246
# Testing on clang using the excellent silkeh clang docker images
212247
clang:

tests/CMakeLists.txt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,12 +370,17 @@ endif()
370370
string(REPLACE "test_" "${CMAKE_CURRENT_SOURCE_DIR}/test_" PYBIND11_ABS_PYTEST_FILES
371371
"${PYBIND11_PYTEST_FILES}")
372372

373+
set(PYBIND11_TEST_PREFIX_COMMAND
374+
""
375+
CACHE STRING "Put this before pytest, use for checkers and such")
376+
373377
# A single command to compile and run the tests
374378
add_custom_target(
375379
pytest
376-
COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES}
380+
COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest
381+
${PYBIND11_ABS_PYTEST_FILES}
377382
DEPENDS ${test_targets}
378-
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
383+
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
379384
USES_TERMINAL)
380385

381386
if(PYBIND11_TEST_OVERRIDE)
@@ -386,6 +391,27 @@ if(PYBIND11_TEST_OVERRIDE)
386391
"Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect")
387392
endif()
388393

394+
# cmake-format: off
395+
add_custom_target(
396+
memcheck
397+
COMMAND
398+
PYTHONMALLOC=malloc
399+
valgrind
400+
--leak-check=full
401+
--show-leak-kinds=definite,indirect
402+
--errors-for-leak-kinds=definite,indirect
403+
--error-exitcode=1
404+
--read-var-info=yes
405+
--track-origins=yes
406+
--suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-python.supp"
407+
--suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-numpy-scipy.supp"
408+
--gen-suppressions=all
409+
${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES}
410+
DEPENDS ${test_targets}
411+
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
412+
USES_TERMINAL)
413+
# cmake-format: on
414+
389415
# Add a check target to run all the tests, starting with pytest (we add dependencies to this below)
390416
add_custom_target(check DEPENDS pytest)
391417

tests/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ pytest==4.6.9; python_version<"3.5"
66
pytest==6.1.2; python_version=="3.5"
77
pytest==6.2.1; python_version>="3.6"
88
scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6"
9-
scipy==1.5.2; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.9"
9+
scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10"

tests/test_virtual_functions.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,7 @@ def dispatch(self):
251251
== 'Tried to call pure virtual function "Base::dispatch"'
252252
)
253253

254-
p = PyClass1()
255-
return m.dispatch_issue_go(p)
254+
return m.dispatch_issue_go(PyClass1())
256255

257256
b = PyClass2()
258257
assert m.dispatch_issue_go(b) == "Yay.."

tests/valgrind-numpy-scipy.supp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Valgrind suppression file for NumPy & SciPy errors and leaks in pybind11 tests
2+
3+
{
4+
Leaks when importing NumPy
5+
Memcheck:Leak
6+
fun:malloc
7+
fun:_PyMem_RawMalloc
8+
fun:PyObject_Malloc
9+
fun:_PyObject_GC_Alloc
10+
fun:_PyObject_GC_Malloc
11+
fun:_PyObject_GC_NewVar
12+
fun:tuple_alloc
13+
fun:PyTuple_Pack
14+
...
15+
fun:__pyx_pymod_exec_*
16+
}
17+
18+
{
19+
Leaks when importing NumPy (bis)
20+
Memcheck:Leak
21+
fun:malloc
22+
fun:_PyMem_RawMalloc
23+
fun:PyObject_Malloc
24+
fun:_PyObject_New
25+
fun:PyCode_NewWithPosOnlyArgs
26+
fun:PyCode_New
27+
...
28+
fun:__pyx_pymod_exec_*
29+
}
30+
31+
{
32+
Leaks when importing NumPy (tris)
33+
Memcheck:Leak
34+
fun:malloc
35+
fun:_PyMem_RawMalloc
36+
fun:PyObject_Malloc
37+
fun:_PyObject_GC_Alloc
38+
fun:_PyObject_GC_Malloc
39+
fun:_PyObject_GC_NewVar
40+
fun:tuple_alloc
41+
fun:_PyTuple_FromArray
42+
fun:_PyObject_MakeTpCall
43+
fun:_PyObject_VectorcallTstate
44+
fun:PyObject_Vectorcall
45+
fun:call_function
46+
fun:_PyEval_EvalFrameDefault
47+
fun:_PyEval_EvalFrame
48+
fun:function_code_fastcall
49+
fun:_PyFunction_Vectorcall
50+
}
51+
52+
{
53+
Leaks when importing NumPy (quater)
54+
Memcheck:Leak
55+
fun:malloc
56+
fun:_PyMem_RawMalloc
57+
fun:PyObject_Malloc
58+
fun:_PyObject_GC_Alloc
59+
fun:_PyObject_GC_Malloc
60+
fun:_PyObject_GC_NewVar
61+
fun:tuple_alloc
62+
fun:PyTuple_New
63+
fun:r_object
64+
fun:r_object
65+
fun:r_object
66+
fun:r_object
67+
}
68+
69+
{
70+
Leaks when importing NumPy (quinquies)
71+
Memcheck:Leak
72+
fun:malloc
73+
fun:_PyMem_RawMalloc
74+
fun:PyObject_Malloc
75+
fun:_PyObject_GC_Alloc
76+
fun:_PyObject_GC_Malloc
77+
fun:_PyObject_GC_NewVar
78+
fun:tuple_alloc
79+
fun:PyTuple_New
80+
fun:dictiter_iternextitem
81+
fun:list_extend
82+
fun:_PyList_Extend
83+
fun:PySequence_List
84+
}
85+
86+
{
87+
Leak when importing scipy.fft
88+
Memcheck:Leak
89+
fun:_Znwm
90+
fun:PyInit_pypocketfft
91+
fun:_PyImport_LoadDynamicModuleWithSpec
92+
fun:_imp_create_dynamic_impl.constprop.3
93+
fun:_imp_create_dynamic
94+
fun:cfunction_vectorcall_FASTCALL
95+
fun:PyVectorcall_Call
96+
fun:_PyObject_Call
97+
fun:PyObject_Call
98+
fun:do_call_core
99+
fun:_PyEval_EvalFrameDefault
100+
fun:_PyEval_EvalFrame
101+
fun:_PyEval_EvalCode
102+
}
103+
104+
{
105+
NumPy leaks when spawning a subprocess
106+
Memcheck:Leak
107+
fun:malloc
108+
...
109+
fun:_buffer_get_info
110+
fun:array_getbuffer
111+
fun:PyObject_GetBuffer
112+
fun:__Pyx__GetBufferAndValidate*
113+
fun:__pyx_f_5numpy_6random_13bit_generator_12SeedSequence_mix_entropy
114+
fun:__pyx_pw_5numpy_6random_13bit_generator_12SeedSequence_1__init__
115+
fun:type_call
116+
fun:__Pyx__PyObject_CallOneArg
117+
fun:__pyx_pw_5numpy_6random_13bit_generator_12BitGenerator_1__init__
118+
}

0 commit comments

Comments
 (0)