Emacs is a beast of many, many faces. No one can really understand the whole thing. There are so many packages, so many functions, so many details… but this post is simple, really: it’s about using Emacs as a debugger for your C/C++ programs.
Surely you can do all of your debugging using GDB directly. GDB is a great program and, once you master it, it’s not that bad. However, having a debugger integrated with your favorite editor is even better: all of your files are already there, it’s orders of magnitude faster to add breakpoints and it’s far more comfortable to step over/into lines of code and see them on the screen, highlighted and all. Most certainly, you have seen how those wicked IDEs work. If you long for their pretty buttons or their “user-friendliness”, there isn’t much Emacs can do for you; if, on the other hand, you just want to debug your code within Emacs and easily set breakpoints and run your programs, then read on!
Let’s say you wrote the following program and stored it in a file named x.cpp:
int i = 3;
std::cout << i << std::endl;
std::cout << i << std::endl;
In order to compile it, you can type
M-x compile (where
M-x most probably means “Alt-x” for you, and where
compile should be followed by Enter) from Emacs and then alter the compile command to something like:
Compile command: g++ -g3 -ggdb -o x x.cpp
-g3 -ggdb part asks GCC to add a lot of debugging information, which is sometimes better than a plain option like
-g. This makes your executable somewhat bigger, but in most cases that isn’t a problem.
Now for the real debugging: GDB is available from Emacs thanks to GUD (Grand Unified Debugger), which you invoke by typing
M-x gdb. You will then be asked to confirm the GDB command line in the minibuffer (the status bar-like area at the bottom). In many cases, GUD will guess the executable file by itself and all you have to do is press Enter; if it guesses wrong or if it shows no guess at all, then just type the path for the executable you want to debug (TAB autocompletion for files works). In our example, the final GDB command line would look like this in the minibuffer:
Run gdb (like this): gdb --annotate=3 x
I think the
--annotate=3 part is there because GUD needs it. I don’t know what happens if you turn it off! In any case, after pressing Enter, you should see GDB starting and finally showing a
(gdb) prompt. You can now use it much in the same way you would if you were in a regular terminal, one notable exception being that you must use Ctrl-Up (not just Up) to navigate through the history.
After GDB has started, take the following steps:
Cis Control). This will split your frame into two, allowing you to view both GDB and your program.
- Visit your file with
- Switch from one window to the other by typing
When running GDB with GUD, you can invoke many GDB commands by using those provided by GUD. So if you’re currently browsing x.cpp, you can set a breakpoint by positioning the point (cursor) in a source code line and typing
M-x gud-break. GUD will communicate with GDB and execute the appropriate command and will also display an arrow (in graphics mode) or a red “B” to the left of the line to indicate there’s a breakpoint there.
All important GUD functions are bound to a key combination. You can check all of them at this page of the Emacs manual. Alternatively, you can set arbitrary key combinations to execute any Emacs function you like, including the GUD ones. If you would like F5 to add a breakpoint to the current line, you can do so by typing
M-x global-set-key. Emacs will first ask you what key you want to bind and then what function that key should be bound to. In this example, you would press F5 and then type in
gud-break. Beware, though, that GDB should be running while you do it, otherwise the
gud-break command may not have yet been loaded.
A final tip: if your debugging often freezes for a few seconds when you try to step into a function (inside or outside Emacs!), try setting the environment variable
LD_BIND_NOW to a non-empty value before running your program. You can do it from within GDB by typing the command:
(gdb) set env LD_BIND_NOW 1