-
-
Notifications
You must be signed in to change notification settings - Fork 205
Testing with sanitizers
The simplest way to test R packages with sanitizers is to build R itself with sanitizers as well.
Basic instructions for building R are here: https://github.com/wch/r-source/wiki
Clone the repo:
git clone https://github.com/wch/r-source.git
cd r-source
Optionally check out the branch corresponding to a specific version.
Get recommended packages:
tools/rsync-recommended
If you have an /.R/Makevars
file, temporarily disable it by renaming it.
Set environment variables for the C, C++ and Objective C compilers. Objective C is only necessary on macOS.
export CC="ccache clang-mp-16 -fsanitize=address" CXX="ccache clang++-mp-16 -fsanitize=address" OBJC="ccache clang-mp-16 -fsanitize=address"
In this example we use MacPorts's version of Clang, named clang-mp-16
. Replace this with the appropriate compiler on your system.
- Using Apple's Clang is not recommended because it does not support LeakSanitizer.
- Instrumentation for LeakSanitizer does not need to be enabled separately, as it is part of AddressSanitizer.
- UndefinedSanitizer is not recommended for general use because it produces many non-useful warnings, especially with older Clang versions or with Apple Clang. It also has a significant performance impact.
- This is the appropriate place to include
ccache
, if desired (recommended).
Note: The sanitizer options must be included here, and not in CFLAGS
, as they are also required in the linking step.
Set environment variables and linking options for the Fortran compiler:
export FC="ccache gfortran-mp-12"
Note: If using Clang as the C/C++ compiler, do not enable any sanitizers for gfortran. Clang's and GCC's sanitizer implementations are not compatible.
On some systems, additional linking options must be set for Fortran. The following example is for MacPorts. Adapt it to your system. The -Wl
part will not typically be necessary.
export FLIBS="-L/opt/local/lib/libgcc -lgfortran -lquadmath -lm -Wl,-rpath /opt/local/lib/gcc12"
Set environment variables for the C preprocessor and linker. These may not be necessary on all systems.
export CPPFLAGS=-I/opt/local/include LDFLAGS="-L/opt/local/lib"
Set C/C++ compilation options. The following options are required for readable sanitizer output. -Og
enables only debugger-friendly optimizations. Without any optimizations at all, R would be painfully slow.
export CFLAGS="-g -fno-omit-frame-pointer -Og"
export CXXFLAGS=$CFLAGS
Configure the build, and set your preferred installation location. Here we use ~/rsan
.
./configure --prefix=$HOME/rsan
If this step errors with missing libraries (for example liblzma
), install them, and double check that the C preprocessor and linker flags are set to the location where the libraries are installed.
Check the output and make sure that the compiler commands are as you expect, and they all include sanitizer flags (except Fortran). An example output:
R is now configured for x86_64-apple-darwin18.7.0
Source directory: .
Installation directory: /Users/szhorvat/rsan
C compiler: ccache clang-mp-16 -fsanitize=address -g -fno-omit-frame-pointer -Og
Fortran fixed-form compiler: ccache gfortran-mp-12 -g -O2
Default C++ compiler: ccache clang++-mp-16 -fsanitize=address -std=gnu++17 -g -fno-omit-frame-pointer -Og
C++11 compiler: ccache clang++-mp-16 -fsanitize=address -std=gnu++11 -g -fno-omit-frame-pointer -Og
C++14 compiler: ccache clang++-mp-16 -fsanitize=address -std=gnu++14 -g -fno-omit-frame-pointer -Og
C++17 compiler: ccache clang++-mp-16 -fsanitize=address -std=gnu++17 -g -fno-omit-frame-pointer -Og
C++20 compiler: ccache clang++-mp-16 -fsanitize=address -std=gnu++20 -g -fno-omit-frame-pointer -Og
C++23 compiler: ccache clang++-mp-16 -fsanitize=address -std=gnu++2b -g -fno-omit-frame-pointer -Og
Fortran free-form compiler: ccache gfortran-mp-12 -g -O2
Obj-C compiler: ccache clang-mp-16 -fsanitize=address -g -O2 -fobjc-exceptions
Interfaces supported: X11, aqua, tcltk
External libraries: pcre2, readline, curl
Additional capabilities: PNG, JPEG, TIFF, NLS, cairo, ICU
Options enabled: shared BLAS, R profiling
Capabilities skipped:
Options not enabled: memory profiling
Recommended packages: yes
Before we can build R, the following must be run (see here):
cd doc/manual && make front-matter html-non-svn
cd ../..
echo -n 'Revision: ' > SVN-REVISION
git log --format=%B -n 1 \
| grep "^git-svn-id" \
| sed -E 's/^git-svn-id: https:\/\/svn.r-project.org\/R\/[^@]*@([0-9]+).*$/\1/' \
>> SVN-REVISION
echo -n 'Last Changed Date: ' >> SVN-REVISION
git log -1 --pretty=format:"%ad" --date=iso | cut -d' ' -f1 >> SVN-REVISION
Now we are ready to run the build. For faster builds, set export MAKEFLAGS=-j8
, then build R using make
.
When the build is done, run make install
to install R in the location specified by the --prefix
option of ./configure
.
Add the location of the newly installed R to your path, export PATH=$HOME/rsan/bin:$PATH
. Start R, evaluate .libPaths()
, and take note of the location of installed packages, in case you need to start with a clean slate later.
Before we build igraph, we must install devtools
using install.packages('devtools')
. This will take considerable time, but only needs to be done once. When the installation is done, quit R. At this point you may see memory leak warnings from LeakSanitizer.
Install the development version of igraph as usual. No special options need to be set to enable sanitizers, as compiler options may be inherited from the R build.
After having built igraph, change the igraph source directory, set the envvar TESTTHAT_PARALLEL=false
to disable parallel testing and esure that all code is run in a single R process, and run the following in a fresh R session:
library(devtools)
test()
q()
Note that LeakSanitizer output will only be shown when R exits, hence the q()
. It is important to run this in a newly started R session, otherwise LeakSanitizer may detect problems from previously run commands, possibly from other packages than igraph.
In order to run only a few test commands, start a new R session, then run:
library(devtools)
load_all()
# your commands
q()
load_all()
will load igraph from the current directory instead of from the installed location. This is necessary in order to get line number references in sanitizer output.