调试说明
本节介绍可用于调试生成代码的编译和执行的技术。
另请参阅
Memcheck
Memcheck 是一个使用 Valgrind 实现的内存错误检测工具。它对于检测编译代码中的内存错误非常有用,特别是越界访问和释放后使用错误。有缺陷或错误编译的本地代码会生成这类错误。Memcheck 文档解释了其用法;此处,我们仅讨论其与 Numba 结合使用的具体细节。
Python 解释器和 Numba 使用的一些库在使用 Memcheck 时可能会产生误报——请参阅本手册的此节,了解有关误报发生原因的更多信息。误报可能使确定实际错误何时发生变得困难,因此抑制已知的误报会有所帮助。这可以通过提供一个抑制文件来完成,该文件指示 Memcheck 忽略其中定义的匹配抑制规则的错误。
CPython 源代码分发版中包含一个抑制文件,位于 Misc/valgrind-python.supp
文件中。使用此文件可以防止 Python 内存分配实现产生大量虚假错误。此外,Numba 仓库中也包含一个抑制文件,位于 contrib/valgrind-numba.supp
。
注意
重要的是要使用您正在使用的 Python 解释器和 Numba 版本的抑制文件——这些文件会随时间演变,因此非当前版本可能无法抑制某些错误,或错误地抑制实际错误。
要在 Memcheck 下运行 Python 解释器并使用这两个抑制文件,请使用以下命令调用它
valgrind --tool=memcheck \
--suppressions=${CPYTHON_SRC_DIR}/Misc/valgrind-python.supp \
--suppressions=${NUMBA_SRC_DIR}/contrib/valgrind-numba.supp \
python ${PYTHON_ARGS}
其中 ${CPYTHON_SRC_DIR}
设置为 CPython 源代码分发的位置,${NUMBA_SRC_DIR}
是 Numba 源代码目录的位置,${PYTHON_ARGS}
是 Python 解释器的参数。
如果存在错误,则描述这些错误的消息将打印到标准错误输出。一个错误示例如下:
==77113== at 0x24169A: PyLong_FromLong (longobject.c:251)
==77113== by 0x241881: striter_next (bytesobject.c:3084)
==77113== by 0x2D3C95: _PyEval_EvalFrameDefault (ceval.c:2809)
==77113== by 0x21B499: _PyEval_EvalCodeWithName (ceval.c:3930)
==77113== by 0x26B436: _PyFunction_FastCallKeywords (call.c:433)
==77113== by 0x2D3605: call_function (ceval.c:4616)
==77113== by 0x2D3605: _PyEval_EvalFrameDefault (ceval.c:3124)
==77113== by 0x21B977: _PyEval_EvalCodeWithName (ceval.c:3930)
==77113== by 0x21C2A4: _PyFunction_FastCallDict (call.c:376)
==77113== by 0x2D5129: do_call_core (ceval.c:4645)
==77113== by 0x2D5129: _PyEval_EvalFrameDefault (ceval.c:3191)
==77113== by 0x21B499: _PyEval_EvalCodeWithName (ceval.c:3930)
==77113== by 0x26B436: _PyFunction_FastCallKeywords (call.c:433)
==77113== by 0x2D46DA: call_function (ceval.c:4616)
==77113== by 0x2D46DA: _PyEval_EvalFrameDefault (ceval.c:3139)
==77113==
==77113== Use of uninitialised value of size 8
提供的回溯信息仅概述了 C 调用堆栈,这可能使得难以确定 Python 解释器在错误发生时正在做什么。通过查看 GNU 调试器 (GDB) 中的回溯,可以了解更多关于堆栈状态的信息。使用附加参数 --vgdb-error=0
启动 valgrind
,并按照输出提示使用 GDB 附加到进程。一旦遇到错误,GDB 将在错误处停止,并且可以检查堆栈。
GDB 确实支持通过 Python 堆栈进行回溯,但这需要符号信息,而这些符号在您的 Python 发行版中可能不容易获得。在这种情况下,仍然有可能确定 Python 中发生了什么的一些信息,但这取决于仔细检查回溯。例如,在与上述错误相对应的回溯中,我们看到如下条目:
#18 0x00000000002722da in slot_tp_call (
self=<_wrap_impl(_callable=<_wrap_missing_loc(func=<function at remote
0x1cf66c20>) at remote 0x1d200bd0>, _imp=<function at remote 0x1d0e7440>,
_context=<CUDATargetContext(address_size=64,
typing_context=<CUDATypingContext(_registries={<Registry(functions=[<type
at remote 0x65be5e0>, <type at remote 0x65be9d0>, <type at remote
0x65bedc0>, <type at remote 0x65bf1b0>, <type at remote 0x8b78000>, <type
at remote 0x8b783f0>, <type at remote 0x8b787e0>, <type at remote
0x8b78bd0>, <type at remote 0x8b78fc0>, <type at remote 0x8b793b0>, <type
at remote 0x8b797a0>, <type at remote 0x8b79b90>, <type at remote
0x8b79f80>, <type at remote 0x8b7a370>, <type at remote 0x8b7a760>, <type
at remote 0x8b7ab50>, <type at remote 0x8b7af40>, <type at remote
0x8b7b330>, <type at remote 0x8b7b720>, <type at remote 0x8b7bf00>, <type
at remote 0x8b7c2f0>, <type at remote 0x8b7c6e0>], attributes=[<type at
remote 0x8b7cad0>, <type at remote 0x8b7cec0>, <type at remote
0x8b7d2b0>, <type at remote 0x8b7d6a0>, <type at remote 0x8b7da90>,
<t...(truncated),
args=(<Builder(_block=<Block(parent=<Function(parent=<Module(context=<Context(scope=<NameScope(_useset={''},
_basenamemap={}) at remote 0xbb5ae10>, identified_types={}) at remote
0xbb5add0>, name='cuconstRecAlign$7',
data_layout='e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64',
scope=<NameScope(_useset={'',
'_ZN08NumbaEnv5numba4cuda5tests6cudapy13test_constmem19cuconstRecAlign$247E5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE',
'_ZN5numba4cuda5tests6cudapy13test_constmem19cuconstRecAlign$247E5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE'},
_basenamemap={}) at remote 0x1d27bf10>, triple='nvptx64-nvidia-cuda',
globals={'_ZN08NumbaEnv5numba4cuda5tests6cudapy13test_constmem19cuconstRecAlign$247E5ArrayIdLi1E1C7mutable7ali...(truncated),
kwds=0x0)
我们可以看到一些参数,特别是已编译函数的名称,例如
_ZN5numba4cuda5tests6cudapy13test_constmem19cuconstRecAlign$247E5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE5ArrayIdLi1E1C7mutable7alignedE
我们可以通过 c++filt
运行此结果,以查看更具可读性的表示形式。
numba::cuda::tests::cudapy::test_constmem::cuconstRecAlign$247(
Array<double, 1, C, mutable, aligned>,
Array<double, 1, C, mutable, aligned>,
Array<double, 1, C, mutable, aligned>,
Array<double, 1, C, mutable, aligned>,
Array<double, 1, C, mutable, aligned>)
这是 JIT 编译函数的完全限定名称及其被调用时所使用的类型。