Gdb Macros for R

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.

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