This post gives you a quick walkthrough how to debug an R package calling native C/C++ code with LLDB.
Jump into this recipe if you feel comfortable enough with lldb. Otherwise, scroll a bit more.
R - d lldb
(lldb) run -e "library(<package.name>)"
(lldb) b <c-call-name>
(lldb) run -f inst/debug.R # (or run -e "...")
Step-by-step debug process
Start R with the debugger:
R -d lldb
Run the program with inside lldb:
As new R process is created, load the package you want to debug:
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(or
b test.c:12in short)
Continue the program with
continuethat puts you back in R
Call the R function having the underlying C function you want to step into with, e.g.
We are again back to lldb and all the breakpoints are set.
You can now examine the environment. If you like, type command
guito start the "magical" terminal interface TUI (or sometimes it's called "LLDB curses GUI")
Examine R objects
At some point, you would like to inspect the variables with R structure. In
the newer versions, you can receive an error like
...has unknown return type; cast the call to its declared return type.
This means that you need to cast the return value
of the variables into a base C type so
lldb can know what they exactly
return. As far as I know, in earlier versions of
lldb, that wasn't a problem
but it's the case for now and I found it quite annoying sigh.
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)) # returns the value
Print the whole
data.frame (casting doesn't matter so can do any e.g.
(lldb) p (char)Rf_PrintValue(df)
Set debug variables
For R objects:
(lldb) p REAL(VECTOR_ELT(df, 1)) (double *) $0 = 0x0e30.. (lldb) p REAL(VECTOR_ELT(df, 1)) (double) $1 = 3.1415.. (lldb) e double $var=REAL(VECTOR_ELT(df, 1)) (lldb) p $var (double) $var = 3.1415
Basic LLDB commands
Help for everything
frame variableto get the current local variable names and their values.
Go to next line with
You can step in a function with
watch set var <varname>Set watchpoints to stop the execution when the value of a variable changes.
watchpoint listlist all the watchpoints.
Run shell commands
- Possible to run Shell commands inside the LLDB (I've almost
never needed this feature though):
platform shell pwd
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, add
CFLAGS=-g -O0for C, and
CXXFLAGS=-g -O0for C++, in the
Most of the instructions here can also be used for debugging R packages with GDB. Check GDB to LLDB command map if use GDB.
Using gdb to debug R packages with native code https://vimeo.com/11937905
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/