Fixing Emacs GDB mode, part 1

I like using use the Emacs gud-mode for my debugging sessions. Unfortunately, it’s pretty quirky in how it assigns source files to Emacs windows. This totally breaks my debugging flow. Today I decided to document this misbehavior and in a later post, explain how to fix it.

The problem occurs when stepping through a program in gud-mode with gdb-many-windows enabled. I’d expect Emacs to assign new source buffers into the dedicated middle-left source window. Instead, source buffers are (randomly?) assigned on top of other GDB buffers like the GDB comint window. The layout problem gets even worse with multiple Emacs frames. Here are my repro steps with a diagram of what happens and how I’d like Emacs to behave instead.

Here are the steps to reproduce the problem on Emacs 24.3.1.

Bind gud-next and gud-step to [f7] and [f8]:

;; This goes into your .emacs
(add-hook 'gud-mode-hook
          '(lambda ()
             (global-set-key (kbd "<f7>") 'gud-next)
             (global-set-key (kbd "<f8>") 'gud-step)))

Build my example program:

git clone https://gist.github.com/d94e2666ae05b58906c6.git emacs-gdb-proj
cd emacs-gdb-proj
make

Start Emacs and enter GDB:

emacs &
M-x gdb
M-x gdb-many-windows

Use *gud-test* comint buffer to set a break point in main() and run the program:

(gdb) b main
Breakpoint 1 at 0x4004b1: file main.c, line 6.
(gdb) r

The window layout should now look something like this (the source window with main.c highlighted):

*gud-test* *locals of test* ./main.c *input/output of test* *stack frames of test* *breakpoints of test*

The contents of main.c look like this:

#include <stdio.h>

int main() 
{
    foo();
    foo();
    foo();
}

If you step into the foo() function with [f8], you’re taken into the b.c source file, which opens as expected into left-middle source window.

However, if instead of directly stepping into foo() you open another source file in the source window, say, C-x C-f’ing the Makefile from the test project and hit [f8] again, BOOM, the b.c buffer opens in the comint *gud-test* window and not in the source window! The window layout now looks like this:

./b.c *locals of test* ./Makefile *input/output of test* *stack frames of test* *breakpoints of test*

Another way to trigger the same:

  1. Start gud-mode + gdb-many-windows as above.
  2. Step into foo().
  3. Open the Makefile into the source window.
  4. Click on any of the functions/source files into bottom-left stack frame window.

Again, the source file opens on top of the upper-left comint window.

I did find some related Stackoverflow posts. This post contains a defadvice trick that seems to work for the latter reproduction steps, but it didn’t make a difference in the former case. Another post discusses a related problem but concludes that there’s no problem if you just use gdb-many-windows.

I hope to resolve this soon and update this post with a solution.