Debugging C code called from R within Emacs

Anybody who tried debugging C code in their R packages knows it can be a real pain. The simple solution is to run "R -d gdb" at the command prompt, but then you're missing on all the advantages that come with a nice editor like Emacs. Follow these instructions to set up your Unix/Linux/OS X system.

First you need to configure the system:

1. Install the ESS module into Emacs (http://ess.r-project.org)

2. Add the following lines to your .emacs, which define functions Rgdb, send-line, send-paragraph and send-region and assign key bindings to them. Note: for some old Emacs versions, you may need to remove the "--debugger-args= -i=mi" from last line in defun Rgdb.

(defun Rgdb ()
  "Start R with gdb"
  (interactive)
  (R-mode)              ;put original window in R-mode
  (let ((name "R -d gdb --debugger-args= -i=mi"))(gud-gdb name))
)

(defun send-line ()
  (interactive)
  (save-excursion
    (beginning-of-line)
    (if (not (= (point) (point-max)))
      (let ((linestr (buffer-substring (progn (end-of-line) (point)) (progn (beginning-of-line) (point)))))
      (other-window 1)
      (end-of-buffer)
      (insert linestr)
      (call-interactively (key-binding "\r"))
      (other-window -1)))))

(defun send-paragraph (&optional arg)
  (interactive "P")
  (let ((beg (progn (backward-paragraph 1) (point)))
  (end (progn (forward-paragraph arg) (point))))
  (copy-region-as-kill beg end))
  (other-window 1)
  (end-of-buffer)
  (yank)
  (call-interactively (key-binding "\r"))
  (other-window -1))

(defun send-region (beg end &optional args) 
  (interactive "r\nP")          
  (copy-region-as-kill beg end)
  (other-window 1)
  (end-of-buffer)
  (yank)
  (call-interactively (key-binding "\r"))
  (other-window -1))

(global-set-key (kbd "C-c l") 'send-line)
(global-set-key (kbd "C-c p") 'send-paragraph)
(global-set-key (kbd "C-c r") 'send-region)



3. Tell R to save debugging information when compiling packages and not to optimize code (otherwise gdb gets confused). To do this, create a directory .R in your home directory and create a Makevars file with the following lines:

CFLAGS = -g -O0 -std=gnu99 -Wall -pedantic
CXXFLAGS = -g -O0 -Wall -pedantic

4. Install Valgrind (http://valgrind.org)

Once the system is properly configured, the usual workflow is as follows:

1. Start Emacs, split the window (C-x 2). On the upper buffer open the files containing R and C code. On the lower buffer type M-x and then Rgdb to start gdb.

2. Set gdb breakpoints as usual. The easiest way is to position the cursor on the desired line of the source code and press C-x space. (note: the first time you run gdb the breakpoint will be dependent on a future library load, this is OK)

3. Type r at the gdb buffer to start R. You can then load your library and execute R code normally. The .emacs file contains shortcuts to send R code from the upper to the lower buffer (C-c l sends a line, C-c p a paragraph, C-c r a region).

4. When execution reaches a C breakpoint, the control will return to gdb. You can then debug as usual in gdb (if you're new to gdb, there's plenty of tutorials online)

5. Once you've detected the error and modified the C code, re-install the library and type "r" at the gdb prompt to re-start R.

6. A big pain with C/C++ code is finding memory leakage and problems with memory pointers. Valgrind can be an invaluable help. The document interfaceCR.pdf (below) has instructions on running Valgrind on R code. In my experience the Valgrind reports are much more useful on Unix/Linux than on OS X.


Last but not least, the most important tip: do not despair, getting started can be frustrating but there's a light at the end of the tunnel (most of the times).

Ċ
David Rossell,
Sep 14, 2012, 3:02 AM
Comments