Sanitizer support

Sanitizers 可以在自定义 Julia 构建中使用,以便更容易地检测 Julia 内部 C/C++ 代码中的某些类型的错误。

Address Sanitizer: easy build

从Julia的源代码检出中,您应该能够构建一个支持Julia和LLVM中的地址清理的版本,如下所示:

$ mkdir /tmp/julia
$ contrib/asan/build.sh /tmp/julia/

在这里,我们选择了 /tmp/julia 作为构建目录,但您可以选择任何您想要的目录。构建完成后,使用 /tmp/julia/julia 运行您希望测试的工作负载。内存错误将导致错误。

如果您需要自定义或进一步的细节,请参阅下面的文档。

General considerations

使用 Clang 的 sanitizers 显然需要你使用 Clang (USECLANG=1),但还有另一个问题:大多数 sanitizers 需要一个运行时库,由主机编译器提供,而 Julia 的 JIT 生成的插装代码依赖于该库的功能。这意味着你的主机编译器的 LLVM 版本必须与 Julia 中使用的 LLVM 库的版本匹配。

一个简单的解决方案是拥有一个专用的构建文件夹,以提供匹配的工具链,通过使用 BUILD_LLVM_CLANG=1 进行构建。然后,您可以通过在覆盖 CCCXX 变量时指定 USECLANG=1,从另一个构建文件夹引用此工具链。

当检测到使用 RTLD_DEEPBIND 打开的共享库时,清理工具会出错(参考:google/sanitizers#611)。由于默认情况下 libblastrampoline 使用 RTLD_DEEPBIND,因此在使用清理工具时,我们需要设置环境变量 LBT_USE_RTLD_DEEPBIND=0

要使用其中一个清理工具,请设置 SANITIZE=1,然后添加您想要使用的清理工具的相应标志。

在 macOS 上,这可能还需要一些额外的标志才能正常工作。总的来说,它可能看起来像这样,加上一个或多个下面列出的 SANITIZE_* 标志:

make -C deps USE_BINARYBUILDER_LLVM=0 LLVM_VER=svn stage-llvm

make -C src SANITIZE=1 USECLANG=1 \
    CC=~+/deps/scratch/llvm-svn/build_Release/bin/clang \
    CXX=~+/deps/scratch/llvm-svn/build_Release/bin/clang++ \
    CPPFLAGS="-isysroot $(xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" \
    CXXFLAGS="-isystem $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1"

(或将这些放入您的 Make.user 中,这样您就不需要每次都记住它们)。

Address Sanitizer (ASAN)

为了检测或调试内存错误,您可以使用 Clang 的 address sanitizer (ASAN)。通过使用 SANITIZE_ADDRESS=1 编译,您可以为 Julia 编译器及其生成的代码启用 ASAN。此外,您还可以指定 LLVM_SANITIZE=1 来清理 LLVM 库。请注意,这些选项会带来较高的性能和内存开销。例如,使用 ASAN 进行 Julia 和 LLVM 的测试使得 testall1 的运行时间增加 8-10 倍,同时内存使用量增加 20 倍(通过使用下面描述的选项,这些值可以分别减少到 3 倍和 4 倍)。

默认情况下,Julia 设置 allow_user_segv_handler=1 ASAN 标志,这是信号传递正常工作的必要条件。您可以使用 ASAN_OPTIONS 环境标志定义其他选项,在这种情况下,您需要重复之前提到的默认选项。例如,通过指定 fast_unwind_on_malloc=0malloc_context_size=2 可以减少内存使用,但会牺牲回溯的准确性。目前,Julia 还设置了 detect_leaks=0,但这应该在未来移除。

Example setup

Step 1: Install toolchain

$TOOLCHAIN_WORKTREE 检出一个 Git 工作树(或创建一个树外构建目录),并创建一个配置文件 $TOOLCHAIN_WORKTREE/Make.user,内容为

USE_BINARYBUILDER_LLVM=1
BUILD_LLVM_CLANG=1

运行:

cd $TOOLCHAIN_WORKTREE
make -C deps install-llvm install-clang install-llvm-tools

将工具链二进制文件安装到 $TOOLCHAIN_WORKTREE/usr/tools

Step 2: Build Julia with ASAN

$BUILD_WORKTREE 检出一个 Git 工作树(或创建一个树外构建目录),并创建一个配置文件 $BUILD_WORKTREE/Make.user,内容为

TOOLCHAIN=$(TOOLCHAIN_WORKTREE)/usr/tools

# use our new toolchain
USECLANG=1
override CC=$(TOOLCHAIN)/clang
override CXX=$(TOOLCHAIN)/clang++
export ASAN_SYMBOLIZER_PATH=$(TOOLCHAIN)/llvm-symbolizer

USE_BINARYBUILDER_LLVM=1

override SANITIZE=1
override SANITIZE_ADDRESS=1

# make the GC use regular malloc/frees, which are hooked by ASAN
override WITH_GC_DEBUG_ENV=1

# default to a debug build for better line number reporting
override JULIA_BUILD_MODE=debug

# make ASAN consume less memory
export ASAN_OPTIONS=detect_leaks=0:fast_unwind_on_malloc=0:allow_user_segv_handler=1:malloc_context_size=2

JULIA_PRECOMPILE=1

# tell libblastrampoline to not use RTLD_DEEPBIND
export LBT_USE_RTLD_DEEPBIND=0

运行:

cd $BUILD_WORKTREE
make debug

构建 julia-debug 并使用 ASAN。

Memory Sanitizer (MSAN)

为了检测未初始化内存的使用,您可以使用 Clang 的 memory sanitizer (MSAN),通过使用 SANITIZE_MEMORY=1 进行编译。

Thread Sanitizer (TSAN)

为了调试数据竞争和其他与线程相关的问题,您可以使用 Clang 的 thread sanitizer (TSAN),通过使用 SANITIZE_THREAD=1 进行编译。