Blog Series Tags

Emacs - Debugging Code under Linux with GDB

Visual Studio provides a very nice developer experience when debugging code. Emacs, on the other hand, doesn’t really shine in this area. It relies heavily the on strong debugging tools that ship with Linux, particularly GDB. While GDB is powerful and extensive, it’s command line interface is dated, and can come as a shock to a developer used to the Visual Studio way of doing things. Let’s take the play program below, which introduces an obvious bug involving an array overflow which causes stack corruption.

#include <iostream>
#include <cstdlib>

using namespace std;

void genRandom(int* arr, int size)
{
    srand (time(NULL));
            
    for (int i = 0; i < size; i++)
        arr[i] = rand() % 100 + 1;
}

int main()
{
    const size_t size = 10;

    int arr[size];    
    genRandom(arr, size + 1);

    for (int i = 0; i < size; i++)
        cout << arr[i] << " ";

    cout << endl;
}

You can compile this code by pressing META x followed by tying compile in the command buffer. When ask for the compile command be sure to add the -g to add debug information into the executable.

g++ -g stack-smash.cpp -o stack-smash

Running this code will now result in the following output.

27 12 92 64 32 58 38 1 67 71 
*** stack smashing detected ***: ./stack-smash terminated
Aborted

Debugging this in Emacs requires us to use gbd, the GNU debugger. This can be launched META x GDB. In the prompt that appears type:

GDB -i=mi stack-smash

The -i=mi flag tells the debugger to run in Machine Interface mode, which has better support for another program (in this instance Emacs) to drive its functions. Hitting enter should start the GDB prompt. Note your program has not yet started, for that you need to type:

start

Optionally followed by any parameters you wish to pass to your program (none in this case). But before doing that let’s place a breakpoint in the code to see what’s happening. You can do this by pressing C-x C-a C-b which is the default binding for set breakpoint on the srand (time(NULL)); line (line 8).

Configuring Visual Studio Keys

Now we’re ready to run through the code so type start. This will result in the debugger hitting a temporary break point at the start of your main program. This is so that you can set further breakpoints if required. To continue you can press C-x C-a C-r, which is a little annoying to be honest, so lets fix that by binding the normal Visual Studio key bindings for continue and a couple of other things to make life easier for us. Add this into your init.el / .emacs file, then type M-x followed by loadfile enter enter Sacrifice Goat[1] to update your runtime config.

;; Visual Studio Debugger Key Bindings
(global-set-key (kbd "<f10>") 'gud-next)
(global-set-key (kbd "<f11>") 'gud-step)
(global-set-key [(f9)] 'gud-break)
(global-set-key [(shift f9)] 'gud-remove)
(global-set-key [(control f5)] 'gud-run)
(global-set-key [(f5)] 'gud-cont)
(global-set-key [(shift f5)] 'gud)

Now you can step through your code like a Visual Studio Pro with F10 and F11, you can use F9 to set breakpoints and Shift F9 to remove them.

To get a little more information on the execution context, you can type M-x followed by GDB-many-windows, which will open up a multiple window view containing details like locals, registers, stack and breakpoints among other things.

You can now see the value of the local variable i in the locals window. To see the array’s contents we can use the GDB command line. This is also a good demonstration of how the GDB command line can give you deeper capabilities than is available under the normal Emacs debugging UI. Type:

disp *arr@12

This tell the debugger to print out the contents of the array pointed to by the arr pointer and to print 12 elements (though the actual number of elements is 10). This way you can see the program write past the end of the array corrupting the stack.

While not as user friendly or easy to learn as Visual Studio, GDB provides a number of powerful features, such as reversible debugging, conditional breakpoints and even embedded python scripting. Granted the usage style takes time and it requires careful consideration before doing something, which is not a bad thing in the long run.

[1]

Animal Sacrifice is optional since Emacs 24.2

This is a post in the Emacs series.