LEGACY Crash Course in Debugging C Programs with Emacs GDB (annotate=3)

I have a constant need to debug specific C programs. Because I have mostly worked in constrained embedded systems Linux environments, in reality I have not had possibility to have a debugger available. So I never learned GDB. But recently I was able to compile the application binaries in a regular 32-bit Linux environment. I was finally able to do debugging with Emacs GDB after learning just one day. This crash course will probably help you if you want to learn GDB debugging with text mode Emacs. We will be using the gdb-many-windows variant layout.

Changelog

2019-04-25: Discovered that Ctrl-x <space> works only on older Emacs GDB modes. Added substitute command that works on both newer and older setups.

Note: I discovered the info line command does not work on recent emacs environments. A solution is currently under search. I’m terribly sorry about the problem. New post will be written for modern emacs + gdb if possible.

Example program

To demonstrate things, lets make a simple, but enough complex test program to demonstrate some basic features of the GDB debugger in Emacs. The program is presented below. The program takes current timestamp and prints it, also telling if the timestamp is odd or even. To complicate the things a bit, one function is in dedicated .c file. Here is the file listing:

ttest_main.c:

#include <stdio.h>
#include <time.h>

#include "ttest_odd_even.h"


int main()
{
  time_t t_time_now = time(NULL);

  printf("Starting\n");
  print_odd_even(&t_time_now);
  printf("Ending\n");

  return 0;
}

ttest_odd_even.h:

#ifndef TTEST_ODD_EVEN_H_
#define TTEST_ODD_EVEN_H_


void print_odd_even(time_t* pt_par_time);

#endif // TTEST_ODD_EVEN_H_

ttest_odd_even.c:

#include <stdio.h>
#include <time.h>

#include "ttest_odd_even.h"


void print_odd_even(time_t* pt_par_time)
{
  unsigned int ui_time = 0;

  if (((*pt_par_time) % 2) == 0)
  {
    ui_time = (unsigned int)(*pt_par_time);
    printf("The time value %u was even!\n",
      ui_time);
  }
  else
  {
    ui_time = (unsigned int)(*pt_par_time);
    printf("The time value %u was odd!\n",
      ui_time);
  }
}

Put the files in /tmp/ and issue the following compilation command:

gcc -g -Wall ttest_odd_even.c ttest_main.c -o ttest

The -g option adds debug symbols. If the file command tells something like the following, the compilation most likely succeeded as we wished:

ttest: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

Starting the Emacs GDB many windows mode

Befor going further, I underline that you should now stay in /tmp/ where the .c and .h files and the binary is. This way Emacs GDB picks up the source files easily. So when debugging, if you don’t see your source files listed, you might be out of your corresponding source directory hierarchy. So, please stay with your source file dirs 🙂

Actually starting the text mode Emacs is easy. The following command works:

emacs -nw

You will probably get a window looking like this:

Now start the gdb mode. Press Alt-x . The bottom of the screen will turn to show M-x. Just write the command

gdb

there for now and press enter:

Emacs will now ask a bit further about how to invoke the gdb. It may suggest the correct binary, but lets do it explicitly to be sure:

What happens now is that Emacs opens plain GDB mode in the main window:

Does not look much like the ordinary debuggers yet, right? Well we will get there soon. Press Alt-x and then write

gdb-many-windows

Tadah! The full many-window Emacs GDB debugger mode opens in its full glory:

In example run below we will tell about the sub-windows.

Elementary debugging of the example program

The focus inside Emacs is originally in the GDB sub-window on top left. What we are interested now is setting a single breakpoint and breaking into it. Most convenient way to add breakpoints is using the GDB window command info line. See that in the source code view on middle left we have automatically the ttest_main.c open. We deliberately placed the print_odd_even() function on different file to demonstrate what happens now.

In the GDB sub-window write the following and press enter:

info line print_odd_even

What happens now is that GDB sub-window positions the caret after beginning of the print_odd_even() function:

What is very impressive that the function is correctly looked up from another file! What we have now done is that we have only located the function of interest. Next we will set the actual breakpoint. For this we want to move to the source code sub-window.

The movement command is

Ctrl-x o

(The last character is lowercase oh). But! For some reason there needs to be a tiny pause between x and o. After issuing correctly Ctrl-x o two times the actual cursor is inside the function. The cursor can be freely positioned on the wanted line. Lets position to the line with unsigned int ui_time= 0;

Then the actual placing of breakpoint. It is done by issuing

Ctrl-x Ctrl-a Ctrl-b

The small pause is again needed between x and spacebar. What happens is that there will be a new big red B indicator prefixed in the code line. Also the GDB sub-window on top left tells about placement of new breakpoint. And finally, the bottom right sub-window about breakpoints and threads contains the new breakpoint. See:

Now we can actually run the program. This can be done in the GDB sub-window top-left. So we need to cycle 3 times with Ctrl-x o . Now in the GDB window we issue the command

run

A split-second later the execution stops on our breakpoint. We can see now see many interesting details of the execution, for example stop position in source code, local variable values, full call stack with complete parameters listed and breakpoint statistics. Example picture:

What we can do now in the GDB window is examine values and continue execution either by steps or continuously. Lets first inspect the value of the variable ui_time by:

print ui_time

We probably get some garbage back because our variable has not yet been assigned with zero. So what we got back was $1 = 2670580. To continue with one code line down (step over/next line), issue

n

Situation is now as follows:

We now see from local values sub-window that ui_time is actually 0. But for demonstration purpose lets do it explicitly with the help of GDB sub-window history. Simply first move the cursor up with arrow key to the print statement position:

Then press enter. Bam! The line from history is executed again:

Fetching commands from history can save tremendous amounts of time. As can be seen the value is now 0.

Another convenient thing in the GDB sub-window is that we can run arbitrary c functions there (if they have been linked in). To print the string presentation of the timestamp given to print_odd_even() we can run:

print (char*)ctime(pt_par_time)

Result is something like the following:

Lets make another test. Lets place another breakpoint in the main function. So in GDB sub-window first run

info line main

Then move to the source code window with pressing two times Ctrl-x o . Go to line printf(“Ending\n”); , then place breakpoint with Ctrl-x Ctrl-a Ctrl-b . Now remember the execution is inside print_odd_even()? We continue the execution now continuously with the command

c

This makes the execution run to the second breakpoint:

To continue the program to the end, just issue another c :

You can quit the session by pressing Ctrl-d and the close Emacs with Ctrl-x Ctrl-c .

More debugging features in brief

It is impractical to to cram more step-by-step instructions for this crash course document, but we may still give a couple of useful hints. If you are debugging a loop-structure with breakpoints, sometimes you want to ignore the breakpoint a number of times. This is done by the command ign <breakpointnumber> <numberoftimestoignore>. For example:

ign 1 1000

One can also set conditions to breakpoints. For example, if we have a counter but we want only to have breakpoint occur every 10th increment, we can do like this:

cond 1 ((ui_counter % 10) == 0)

One important command was left out before now. It is the step into command. It is executed with

s

The step into command digs inside subfunction calls if such exist on the current line.

Quick Emacs GDB cheat sheet

Program compilation with debug symbols

gcc -g -Wall ttest_odd_even.c ttest_main.c -o ttest

Opening the full GDB debug mode in Emacs

emacs -nw
Alt-x gdb
Alt-x gdb-many-windows

Moving around

Move to next sub-window

Ctrl-x o

Move source code sub-window to function definition

info line function_name

Breakpoints

Adding in source code

Ctrl-x Ctrl-a Ctrl-b

Only break #1 every 10th time

cond 1 ((ui_counter % 10) == 0)

Delete #1

del 1

Ignore #1 for 35 times

ign 1 35

Disable #1 temporarily

dis 1

Enable #1 again

en 1

Execution control

Run

run

Run with arguments

run foo bar baz

Run with stdin via pipe

run < /tmp/input.txt

Stop execution

Ctrl-c Ctrl-c

Send signal while stopped

signal SIGKILL

Continue execution

c

Step over line

n

Step over 13 lines

n 13

Step into

s

Step out

fin

Examining values

Normal print

print ui_counter

Print with helper functions

print (char*)ctime(pt_par_time)

Stopping debugging

Stopping GDB while after program execution end

Ctrl-d

Closing Emacs

Ctrl-x Ctrl-c

 

Leave a Reply

Your email address will not be published.