R package的UBSAN测试

R package的UBSAN测试
How to perform UBSAN tests on R packages

最近需要提交一个R package (seqminer)到CRAN,我们需要保证这个包代码通过UBSAN 测试(Undefined bahavior sanitizer tests,详见这个链接).
这个测试可以有效的检测结果不确定的指令(例如打印INT_MAX+1)。

目前只有Clang(版本3.3以后)才能支持UBSAN。为了对R package进行UBSAN测试,我们首先需要一个特殊的R版本,即用打开UBSAN支持(-fsanitize=undefined)的Clang编译的R。参考了网上一篇Blog,写一下简单步骤:

1. 下载编译工具

sudo apt-get install valgrind subversion r-base-dev clang-3.4 texlive-fonts-extra texlive-latex-extra
sudo apt-get build-dep cran-base

2. 下载R源代码(devel版本)

svn co https://svn.r-project.org/R/trunk ~/R-devel

3. 替换R-devel/config.site

CC="clang -std=gnu99 -fsanitize=undefined"
CFLAGS="-fno-omit-frame-pointer -Wall -pedantic -mtune=native"
F77="gfortran"
LIBnn="lib64"
LDFLAGS="-L/usr/local/lib64 -L/usr/local/lib"
CXX="clang++ -std=c++11 -fsanitize=undefined"
CXXFLAGS="-fno-omit-frame-pointer -Wall -pedantic -mtune=native"
FC=${F77}

4. 编译

cd R-devel
./configure --with-x=no --without-recommended-packages
make

这里–without-recommended-packages会省略一些R的packages(例如boot,nlme,Matrix) 以提升编译速度。如果不用这个选项,我发现Matrix package总是没法编译

5. 建立~/.R/Makevars

CC = clang -std=gnu99 -fsanitize=undefined -fno-omit-frame-pointer
CXX = clang++ -fsanitize=undefined -fno-omit-frame-pointer
PKG_LIBS = /usr/lib/llvm-3.4/lib/clang/3.4/lib/linux/libclang_rt.ubsan_cxx-x86_64.a

这里CXX 一定要用clang++,如果用clang的话会出现链接错误,比如找不到这个符号: _ZTVN10__cxxabiv117__class_type_infoE

这里还需要指定PKG_LIBS包括LLVM的库文件,不然就找不到UBSAN的符号。

6. 测试package

R CMD build seqminer/
R CMD check seqminer_2.7.tar.gz

UBSAN测试的结果会显示在:seqminer.Rcheck/tests/tests.Rout
如果有错误,你会看很长的一行, runtime error….

其他技巧

检测内存还可以用valgrind:

R CMD check --use-valgrind seqminer_2.7.tar.gz
R -d valgrind --vanilla < mypkg-Ex.R
R -d "valgrind --tool=memcheck --leak-check=full" --vanilla < mypkg-Ex.R