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