When debugging R interactively, one hurdle to navigate is unwrapping SEXP
objects to get at the inner data. Gdb has some useful macro functionality that allows you to wrap useful command sequences in reusable chunks. I recently put together the following macro that attempts to extract and print some useful info from a SEXP
object.
It can be used as follows. For instance, given a SEXP
called “e”:
(gdb) dumpsxp e
Type: LANGSXP (6)
Function:Type: SYMSXP (1)
"< -"
Args:Type: LISTSXP (2)
(SYMSXP,LISTSXP)
We can see that e is a LANGSXP
, and the operator is “< -
". Functions have different components - here we can see the function representation (the SYMSXP
) and the function arguments (the LISTSXP
).
Some knowledge of LANGSXP
structure is useful here. For instance, if we know that for a LANGSXP
that CAR(x)
gives us the function and CDR(x)
gives us the arguments, we can view the components individually.
To see the first component:
(gdb) dumpsxp CAR(e)
Type: SYMSXP (1)
"< -"
The arguments are given by the CDR
of e. We can then crack open the list and view the function arguments, recursively looking through the pairlist until we get to the end:
(gdb) dumpsxp CDR(e)
Type: LISTSXP (2)
(SYMSXP,LISTSXP)
(gdb) dumpsxp CADR(e)
Type: SYMSXP (1)
"x"
(gdb) dumpsxp CADDR(e)
Type: LANGSXP (6)
Function:Type: SYMSXP (1)
"sin"
Args:Type: LISTSXP (2)
(REALSXP,NILSXP)
(gdb) dumpsxp CADDDR(e)
Type: NILSXP (0)
The NILSXP
tells us that we’ve got to the end of the list.
The GDB macro is below. Put it in your .gdbinit to automatically load it when gdb starts up.
[sourcecode language=”c”]
define dumpsxp
if $arg0==0
printf "uninitialized variable\n"
return
end
set $sexptype=TYPEOF($arg0)
# Typename
printf "Type: %s (%d)\n", typename($arg0), $sexptype
# SYMSXP
if $sexptype==1
# CHAR(PRINTNAME(x))
print_char PRINTNAME($arg0)
end
# LISTSXP
if $sexptype==2
printf "(%s,%s)\n", typename(CAR($arg0)), typename(CDR($arg0))
end
# CLOSXP
if $sexptype==3
dumpsxp BODY($arg0)
end
# PROMSXP
# Promises contain pointers to value, expr and env
# tmp = eval(tmp, rho);
if $sexptype==5
printf "Promise under evaluation: %d\n", PRSEEN($arg0)
printf "Expression: "
dumpsxp ($arg0)->u.promsxp.expr
# Expression: (CAR(chain))->u.promsxp.expr
end
# LANGSXP
if $sexptype==6
printf "Function:"
dumpsxp CAR($arg0)
printf "Args:"
dumpsxp CDR($arg0)
end
# SPECIALSXP
if $sexptype==7
printf "Special function: %s\n", R_FunTab[($arg0)->u.primsxp.offset].name
end
# BUILTINSXP
if $sexptype==8
printf "Function: %s\n", R_FunTab[($arg0)->u.primsxp.offset].name
end
# CHARSXP
if $sexptype==9
printf "length=%d\n", ((VECTOR_SEXPREC)(*$arg0))->vecsxp.length
#print_veclen $arg0
print_char $arg0
end
# LGLSXP
if $sexptype==10
set $lgl=*LOGICAL($arg0)
if $lgl > 0
printf "TRUE\n"
end
if $lgl == 0
printf "FALSE\n"
end
end
# INTSXP
if $sexptype==13
printf "%d\n", *(INTEGER($arg0))
end
# REALSXP
if $sexptype==14
print_veclen $arg0
print_double $arg0
end
# STRSXP
if $sexptype==16
print_veclen $arg0
set $i=LENGTH($arg0)
set $count=0
while ($count < $i)
printf "Element #%d:\n", $count
dumpsxp STRING_ELT($arg0,$count)
set $count = $count + 1
end
end
# VECSXP
if $sexptype==19
print_veclen $arg0
end
# RAWSXP
if $sexptype==24
print_veclen $arg0
end
end
define print_veclen
printf "Vector length=%d\n", LENGTH($arg0)
end
define print_char
# this may be a bit dodgy, as I am not using the aligned union
printf "\"%s\"\n", (const char*)((VECTOR_SEXPREC *) ($arg0)+1)
end
define print_double
printf "%g\n", (double*)((VECTOR_SEXPREC *) ($arg0)+1)
end
[/sourcecode]