It can be very difficult to debug compiled code when linked within R. The goal of this section is to show how this process can be made easier with the use of some of the c++ tool chain. We'll cover the following:
assert
to check catch issues in non production phasegdb
to find where a crash happensLet's write a simple RcppGSL function that performs a spline interpolation. The error will happen because we are going to ask for an evaluation that is outside the support.
#include<iostream>
using namespace std;
RcppExport SEXP cpp_lagrWithEffort( SEXP R_args){
BEGIN_RCPP
bool crashit = Rcpp::as<bool>(R_args);
if (crashit) {
int x = 7;
int *p = 0;
cout << "x = " << x;
cout << "The pointer points to the value " << *p;
} else {
cout << "you are lucky." << endl;
}
return(NULL);
END_RCPP
}
I recommend using the following simple macro. Copy the code snippet into an assert.h
file that you can then include in your code.
#ifndef DEBUG
#define ASSERT(x)
#else
#define ASSERT(x) \
if (! (x)) \
{ \
cout << "ERROR!! Assert " << #x << " failed\n"; \
cout << " on line " << __LINE__ << "\n"; \
cout << " in file " << __FILE__ << "\n"; \
}
#endif
Then you can populate your code with various assertion commands.
Start R in debug mode using
R -d gdb -e "source('tmp.r')"
you will then get a prompt. Type run
at the prompt. When the program crashes, you can display the stack by calling backtrace
.
When you find out the segfault, declare some breakpoints right before it crashes, for example here I catcth the call to exit
:
break exit
Then run
again, the program will break at the breakpoint. You can use step
to step through the program or use next
without stepping into functions.
It is important to remember compiling the code with the flags -rdynamic -g
to get correct information. Later on for speed the code should be recompiled without the -g
file and probably using the -O3
flag.
You can type CXXFLAGS = -rdynamic -g
into ~/.R/Makevars
.
tbd