Concept → IO ()

Linux Emacs Coding Music Links About Search

Debugging with Emacs and GDB

Introduction

Erroneous source code can be a nasty issue to tackle. Print statements that inform you about the states of variables are handy but sometimes they are unable to represent the intrinsic structure of the code. Debuggers allow you to run your code step by step, display and watch variables and see what is going on inside another program while it executes.

The GNU poject debugger

Here, we will use the GNU project debugger (GDB), because it is freely available, very actively developed and can be used on many operating systems. It can

GDB in Emacs

To debug our program, we will use Emacs and C source code, although gdb can be used from the command line and can debug many different languages.

A good overview about debugging in Emacs with GDB can be found in the Emacs manual.

A short step-by-step guide:

From withing the Grand Unified Debugger (GUD) buffer, you can run (r) the program to its end or to the first break point, start (start) and run in until the beginning of the main procedure. You can execute the program line-by-line (n) or step into functions with s.

Breakpoints can be set from withing the source buffer by clicking on the fringe or with C-x C-a C-b.

Customize GDB

To permanently set the nice GDB user interface layout, put

(setq gdb-many-windows t)

into your .emacs file. I have also written a function, that facilitates the starting of the debugger:

(defvar gdb-my-history nil "History list for dom-gdb-MYPROG.")
(defun dom-gdb-MYPROG ()
  "Debug MYPROG with `gdb'."
  (interactive)
  (let* ((wd "/path/to/working/directory")
         (pr "/path/to/executable")
         (dt "/path/to/datafile")
         (guess (concat "gdb -i=mi -cd=" wd " --args " pr " -s " dt))
         (arg (read-from-minibuffer "Run gdb (like this): "
                                    guess nil nil 'gdb-my-history)))
    (gdb arg)))

Window management

Upon debugging a program with many source files, GDB displays new source files in (random?) windows in your Emacs frame. This is especially tedious if you use gdb-many-windows. I have written a function dom-gdb-restore-windows, that resets the display and fixes the window layout:

(defun dom-gdb-restore-windows ()
  "Restore GDB session."
  (interactive)
  (if (eq gdb-many-windows t)
      (gdb-restore-windows)
    (dom-gdb-restore-windows-gud-io-and-source)))

(defun dom-gdb-restore-windows-gud-io-and-source ()
  "Restore GUD buffer, IO buffer and source buffer next to each other."
  (interactive)
  ;; Select dedicated GUD buffer.
  (switch-to-buffer gud-comint-buffer)
  (delete-other-windows)
  (set-window-dedicated-p (get-buffer-window) t)
  (when (or gud-last-last-frame gdb-show-main)
    (let ((side-win (split-window nil nil t))
          (bottom-win (split-window)))
      ;; Put source to the right.
      (set-window-buffer
       side-win
       (if gud-last-last-frame
           (gud-find-file (car gud-last-last-frame))
         (gud-find-file gdb-main-file)))
      (setq gdb-source-window side-win)
      ;; Show dedicated IO buffer below.
      (set-window-buffer
       bottom-win
       (gdb-get-buffer-create 'gdb-inferior-io))
      (set-window-dedicated-p bottom-win t))))

(defun dom-gdb-display-source-buffer ()
  "Display gdb source buffer if it is set."
  (interactive)
  (when (or gud-last-last-frame gdb-show-main)
    (switch-to-buffer
     (if gud-last-last-frame
         (gud-find-file (car gud-last-last-frame))
       (gud-find-file gdb-main-file))))
  (delete-other-windows))