This post gives you a quick walkthrough how to debug an R package calling native C/C++ code with LLDB1.
General notes
-
LLDB prints expressions starting with dollar sign
$
. This is useful as we can set conditions or breakpoints, when we want to refer back to that variable. -
"... was compiled with optimization - stepping may behave oddly; variables may not be available."
LLDB cannot do one-to-one mapping when the compiler optimization is on. To overcome this, you should have separate 'debug' builds with the optimization turned off For the R packages, addCFLAGS=-g -O0
for C, andCXXFLAGS=-g -O0
for C++, in the~/.R/Makevars
file. -
Most of the instructions here can also be used for debugging R packages with GDB. Check GDB to LLDB command map for GDB.
-
Start R with the debugger:
R -d lldb
-
Run the program with inside lldb:
run
-
As new R process is created, load the package you want to debug:
library(<package.name>)
-
Exit R by CTRL+C that returns back to lldb
-
Set breakpoints with
b <c-call-name>
. Alternatively, you can also set breakpoints at line numbers:breakpoint set --file test.c --line 12
(orb test.c:12
in short) -
Continue the program with
c
orcontinue
that puts you back in R -
Call the R function having the underlying C function you want to step into with, e.g.
testFun(rnorm(10))
-
We are again back to lldb and all the breakpoints are set.
-
You can now examine the environment. If you like, type command
gui
to start the "magic" terminal user interface TUI (or sometimes it's called "LLDB curses GUI")
Examine R objects
In order to examine R objects, you need to cast the return value of the
variables into a base C type so lldb
can know what they exactly return.
Otherwise, you receive an error like ...has unknown return type; cast the call
to its declared return type
.
Print and inspect R structures interactively, assuming that you want to inspect a R numeric vector named x:
(lldb) p (double)Rf_PrintValue(x)
(lldb) p (bool)Rf_isReal(x)
(lldb) p (double*)REAL(x) # returns the address
(lldb) p ((double*)REAL(x))[2] # returns the value
Print the whole data.frame
(casting doesn't matter so can do any e.g. char
,
double
etc.):
(lldb) p (char)Rf_PrintValue(df)
To set temporary variables within lldb. For R objects:
(lldb) p REAL(VECTOR_ELT(df, 1))
(double *) $0 = 0x0e30..
(lldb) p REAL(VECTOR_ELT(df, 1))[0]
(double) $1 = 3.1415..
(lldb) e double $var=REAL(VECTOR_ELT(df, 1))[0]
(lldb) p $var
(double) $var = 3.1415
Resources
-
Matloff, N. (2011). The Art of R programming: A tour of statistical software design. No Starch Press. Debugging C code in R.
-
Wickham, H. (2015). R packages: organize, test, document, and share your code. O'Reilly Media, Inc. Debugging compiled code. https://r-pkgs.org/src.html#src-debugging
-
Debugging C/C++ code. Bioconductor.org https://www.bioconductor.org/developers/how-to/c-debugging/
-
Although this content is mainly for LLDB, there is a lot of overlapping stuff with GDB. ↩