Last active
March 19, 2019 15:27
-
-
Save knausb/0787d74bdbe3195cd02a48d30ee9c73b to your computer and use it in GitHub Desktop.
rocker_asan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.Rproj.user | |
.Rhistory | |
.RData | |
.Ruserdata | |
*.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
title: Address sanitizer in Rocker | |
author: Brian J. Knaus | |
license: GPL (>= 2) | |
output: | |
html_document: | |
toc: true | |
toc_float: true | |
--- | |
I was informed by CRAN that my R package had a *stack overflow* problem and that I needed to address this. | |
This error was not produced during typical compilation or when using valgrind. | |
I was pretty sure my only path was to use the address sanitizer. | |
However, I had no experience doing this sort of thing. | |
Here I've tried to document the process I took with the hope it may help others. | |
## Why Rocker? | |
CRAN has documentated use of the address sanitizer in their Writing R extensions document [Using-Address-Sanitizer](https://cran.r-project.org/doc/manuals/r-devel/R-exts.html#Using-Address-Sanitizer). | |
An important part of enabling the address sanitizer is that R must be built from source with this option enabled. | |
This is a bit of work. | |
Docker provides the option of creating a containerized version of R, independent of your system R, for development. | |
Rocker contains Docker images that are relevant to R, including ones built with the address sanitizer. | |
So this appears to be an excellent option. | |
## Docker | |
The [Docker site](https://docs.docker.com/) has information on installing Docker. | |
Here we'll assume you have Docker installed. | |
In Debian-like Linux (including Ubuntu) this can be done as follows. | |
```{bash, eval = FALSE} | |
sudo apt install docker.io | |
``` | |
Once installed, you'll have to make sure the daemon is running. | |
This can be accomplished with tht below commands. | |
```{bash, eval = FALSE} | |
service docker status | |
service docker start | |
service docker stop | |
``` | |
Once running you can test your Docker system with the below commands. | |
```{bash, eval = FALSE} | |
sudo docker version | |
sudo docker ps # Installed images. | |
sudo docker run hello-world | |
``` | |
Help can be found by adding 'help' to a command. | |
```{bash, eval = FALSE} | |
sudo docker --help | |
sudo docker run --help | |
``` | |
And search for rocker on Docker Hub. | |
```{bash, eval = FALSE} | |
sudo docker search rocker | |
``` | |
When we find an image we're interested in we can run it with the following. | |
```{bash, eval = FALSE} | |
sudo docker run --name=my-r-base --rm -ti rocker/r-base /bin/bash | |
``` | |
This command included a few options. | |
The `run` command runs the image. | |
If it is not found locally, or needs to be updated, it is done automatically for you. | |
The `--name=` option allows you to assign a name to the container. | |
If you don't assign one it will be done automatically for you. | |
The `--rm` option automatically removes the container for you when you exit. | |
The `-ti` options allocates a pseudo-TTY and provides an interactive session. | |
The name of the image we want to run as a container is `rocker/r-base` and once its started we'll want to execute `/bin/bash` (our shell). | |
When we've finished our work we can quit with the following. | |
```{bash, eval = FALSE} | |
quit | |
``` | |
Note that when we quit that no changes were made to the image. | |
If you decide you would like to remove an image it can be done using `rmi`. | |
```{bash, eval = FALSE} | |
sudo docker rmi rocker/r-base | |
``` | |
We typically want to share some data between our host and our container. | |
Using the `-v` option we can mount a host directory. | |
Here mount the git repository for my R package as a data volume in the container. | |
```{bash, eval = FALSE} | |
sudo docker run --name=my-r-base -v ~/gits/vcfR:/RSource/vcfR --rm -ti rocker/r-base /bin/bash | |
``` | |
More help on Docker can be found here. | |
[understanding-docker](https://docs.docker.com/engine/understanding-docker/) | |
[Find and run the whalesay image](https://docs.docker.com/engine/getstarted/step_three/) | |
[dockervolumes](https://docs.docker.com/engine/tutorials/dockervolumes/) | |
## Rocker | |
The Rocker project includes containers for running R. | |
Some of this is documented here: [Docker for R](http://dirk.eddelbuettel.com/blog/2014/10/23/). | |
There is also a [Rocker Github](https://github.com/rocker-org/rocker) site for the images and documentation is on the [Rocker wiki](https://github.com/rocker-org/rocker/wiki). | |
```{bash, eval = FALSE} | |
sudo docker run --name=my-r-devel-san -v ~/gits/vcfR:/RSource/vcfR --rm -ti rocker/r-devel-san /bin/bash | |
``` | |
## Test address sanitizer (ASAN) | |
Before we invest time in trying to get the address wsanitizer to work within R its a good idea to validate it is working independently of R. | |
This also provides us with an exampel of its use and behaviour. | |
On my container we did not appear to have the required software. | |
We can check for the software as follows. | |
```{bash, eval = FALSE} | |
which llvm-symbolizer-3.8 | |
``` | |
Note that your version number may be different than reported here. | |
Tab completion works here, if llvm-symbolizer is installed. | |
So you really only need to type the begining of the word. | |
If 'which' doesn't find anything then you probably need to install it. | |
To install the software we can use `apt`. | |
```{bash, eval = FALSE} | |
apt-get install llvm | |
``` | |
```{bash, eval = FALSE} | |
ln -s /usr/bin/llvm-symbolizer-3.8 /usr/bin/llvm-symbolizer | |
``` | |
A nice example for learning ASAN can be found [here](http://tsdgeos.blogspot.com/2014/03/asan-and-gcc-how-to-get-line-numbers-in.html). | |
This example is based on it. | |
First, we'll create a text file. | |
```{bash, eval = FALSE} | |
cat main.cpp | |
int main(int, char **) | |
{ | |
int a[3]; | |
a[3] = 4; | |
return 0; | |
} | |
``` | |
Then build it with options to enable the address sanitizer. | |
```{bash, eval = FALSE} | |
g++ -fno-omit-frame-pointer -fsanitize=address main.cpp | |
``` | |
And execute. | |
```{bash, eval = FALSE} | |
./a.out | |
``` | |
At which point a large amount of error information should be printed to the screen. | |
This illustrates an important difference between using ASAN relative to looking for compile time errors or using valgrind: the program compiles and it is not until execution that it throws the error. | |
In an R package this means that the package may build but the error may not be be generated until the running of the vignettes, examples or unit tests. | |
Another issue this should point out is that no line numbers are reported to help us find the error in our source code. | |
In theory, this is an option we should be able to turn on. | |
For me, adding `-g3` to increase the debug level seemed to work. | |
```{bash, eval = FALSE} | |
g++ -g3 -fno-omit-frame-pointer -fsanitize=address main.cpp | |
``` | |
And I received line numbers. | |
## Installing software | |
Once running I typically find I need to install a few extra application. | |
This is done in a similar fashion as you would on a local machine. | |
```{bash, eval = FALSE} | |
apt-get install qpdf | |
# apt-get install pandoc | |
# apt-get install llvm | |
``` | |
The installation of llvm should help make our address sanitizer errors more readable. | |
We can then enter R to install packages. | |
Note that here we'll use the development verions instead of the stable version by using `RD`. | |
```{bash, eval = FALSE} | |
RD | |
``` | |
Now we can install packages as usual. | |
```{bash, eval = FALSE} | |
install.packages('poppr') | |
install.packages('Rcpp') | |
install.packages('knitr') | |
install.packages('memuse') | |
install.packages('rmarkdown') | |
install.packages('pinfsc50') | |
install.packages('viridisLite') | |
install.packages('testthat') | |
install.packages('tidyr') | |
``` | |
Once we have all the dependencies we think we need we can exit to the shell. | |
Or we could install the packages from the command line. | |
```{bash, eval = FALSE} | |
R -e 'install.packages(c("poppr", "Rcpp", "knitr", "memuse", "rmarkdown", "pinfsc50", "viridisLite", "testthat", "tidyr"))' | |
``` | |
Once we have the required dependencies we can build and check our package. | |
```{bash, eval = FALSE} | |
# ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.8 | |
# ln -s /usr/bin/llvm-symbolizer-3.8 /usr/bin/llvm-symbolizer | |
# ASAN_OPTIONS=symbolize=1 | |
# ASAN_SYMBOLIZER_PATH=$(which llvm-symbolizer) | |
``` | |
## Build and test package | |
If you have not mounted a volume containing the package you could download it from CRAN. | |
```{bash, eval = FALSE} | |
mkdir RSource | |
cd RSource | |
# wget https://cran.r-project.org/src/contrib/vcfR_1.4.0.tar.gz | |
tar -xvzf vcfR_1.4.0.tar.gz | |
cd .. | |
``` | |
Build and test the package as follows. | |
```{bash, eval = FALSE} | |
RD CMD build /RSource/vcfR | |
# RD CMD check --as-cran vcfR_1.4.0.9000.tar.gz | |
# RD CMD check --no-build-vignettes vcfR_1.4.0.9000.tar.gz | |
``` | |
Hmm, periods in code chuncks appear to be a problem. | |
Commenting the lines circumvents the issue for now. | |
I use `testthat` for unit testing. | |
One of my tests failed. | |
I can find that output in my tests directory. | |
```{bash, eval = FALSE} | |
less vcfR.Rcheck/tests/testthat.Rout.fail | |
``` | |
This should allow us to reproduce any error reported to us from CRAN. | |
If we can reproduce the error we should be able to fix it. | |
If there is a question about how the compiler was called we can check in the log files. | |
In the file `00install.out` we can validate that the compilation included the correct options. | |
```{bash, eval = FALSE} | |
-fsanitize=address -fno-omit-frame-pointer | |
``` | |
In order to isolate the problem we can install the built package and try to execute the script that threw the error. | |
```{bash, eval = FALSE} | |
# R CMD INSTALL vcfR_1.4.0.9000.tar.gz | |
``` | |
```{bash, eval = FALSE} | |
# Rscript RSource/vcfR/tests/testthat/test_3_extract_gt.R | |
RD CMD BATCH 'RSource/vcfR/tests/testthat/test_3_extract_gt.R' | |
``` | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Version: 1.0 | |
RestoreWorkspace: Default | |
SaveWorkspace: Default | |
AlwaysSaveHistory: Default | |
EnableCodeIndexing: Yes | |
UseSpacesForTab: Yes | |
NumSpacesForTab: 2 | |
Encoding: UTF-8 | |
RnwWeave: Sweave | |
LaTeX: pdfLaTeX |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment