Tutorial: Emacs for Programmers

 in
Ever wanted an all-in-one program development, compilation, and debugging environment? Look no further than Emacs.
Running gdb

gdb is the GNU debugger. It is indispensable for run-time debugging for programs written in nearly any compiled language, most notably C. gdb can also be used for post-mortem examination of a crashed program using a core file.

Not surprisingly, Emacs provides a number of features which allow you to run gdb within an Emacs buffer, interacting with the corresponding source buffers to view and edit code. While gdb deserves a tutorial of its own, here we will introduce you to the Emacs-specific gdb features. gdb provides extensive online help, which can fill in the gaps left here. For the rest of this tutorial, we assume that you have basic familiarity with gdb, or a similar debugger such as dbx.

Let's take the following short program, which will un-doubtedly cause a segmentation fault on most systems:

 #include
int main(void) {
  int i; int *data = NULL;
  data[0] = 1;
  data[1] = 2;
  for (i = 2; i > 30; i++) {
    data[i] = data[i-1] + data[i-2];
  }
  printf("Last value is %d0,data[29]);
}

As you can see, we're attempting to write data into a NULL pointer. Sure enough, when we compile and run the program, we obtain:

loomer:~/test/lj/crashme% ./crashme
Segmentation fault (core dumped)

Let's use gdb to inspect the problem. Using M-x gdb gives us the prompt:

Run gdb (like this): gdb

Here, you should complete the gdb command line. In this case, we want to run gdb on the executable crashme, with the core file core. So, we complete as so:

Run gdb (like this): gdb crashme core

Emacs should open two windows—one containing the gdb interaction session, and the other containing the source file crashme.c. The gdb session will look something like:

GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details.

GDB 4.12,
Copyright 1994 Free Software Foundation, Inc...
Core was generated by "crashme".
Program terminated with signal 11, Segmentation fault.
#0  0x22bc in main () at crashme.c:5
(gdb)

We can now issue gdb commands to inspect the crash. Immediately, we notice that the crashme.c buffer contains an arrow pointing to the current source line, as so:

=>data[0] = 1;
  data[1] = 2;
  /* ... */

This arrow is not part of the source text. It can't be selected, modified, or deleted. You are free to edit the code in the source buffer; this imaginary arrow will not be saved with the edited code. The arrow only exists to let us know what gdb's idea of the current source line is. Note, however, that adding or deleting lines from the source buffer will cause gdb's information about the location of source lines to be out-of-sync with the actual code.

We can see that the crash was caused by a segmentation fault on line 5, pointed to by the arrow. Using the where command in the gdb buffer will give us a stack trace, and so on. You can correct the code in the source buffer, recompile, test, and re-run gdb (if necessary), all within Emacs.

gdb can also be used to inspect running programs. For example, we can run crashme under gdb's control, and step along a line at a time. First, however, let's correct the bug by changing the definition of data to

int data[30];

(Otherwise, crashme would crash on the first line of code, and we'd have scant little to go on by way of demonstration.)

First, we should set a breakpoint on the first line of code. Within the gdb buffer, we can use list to display the first few lines:

 (gdb) list
1       #include <stdio.h>
2       int main(void) {
3         int i;
4         int data[30];
5
6         data[0] = 1;

The command break 6 will set a breakpoint at line 6:

(gdb) break 6
Breakpoint 1 at 0x22b0: file crashme.c, line 6.

Now, the run command will begin execution of the program, but will halt immediately on the first line of code. A buffer for crashme.c will be opened, with our friendly arrow pointing at the line containing the breakpoint.

Now, we can employ gdb's various commands directly, by entering them in the gdb buffer—or, we can use the Emacs key equivalents. Placing point on a line of code in the source buffer and typing C-x C-a C-b will set a breakpoint at that line. Similarly, C-x C-a C-d will delete all breakpoints on that line. (The gdb command info break will list the current breakpoints.) After setting a breakpoint, you can use C-x C-a C-r to resume execution.

All of the above commands can be used within the gdb buffer as well, using C-c instead of C-x C-a as the prefix. For convenience, C-x SPC in either buffer will set a breakpoint on the current source line.

If you find these key bindings unnecessarily lengthy, as I do, you might consider rebinding the functions gud-break, gud-remove, and gud-cont within c-mode-map. For example, I use the commands

(define-key c-mode-map "\M-b" 'gud-break)
(define-key c-mode-map "\M-d' 'gud-remove)
(define-key c-mode-map "\M-r" 'gud-cont)

Of course, this negates the previous meanings of M-b, M-d, and M-r within C Mode.

The following additional macros are available within the gdb buffer, as well as within C Mode by changing the prefix from C-c to C-x C-a.

  • `C-c C-s' Step one line of code, descending into function calls. (The gdb step command.)

  • `C-c C-n' Step one line of code, without descending into function calls. (The gdb next command.)

  • `C-c <' Move up one stack frame. (The gdb up command.)

  • `C-c >' Move up one stack frame. (The gdb down command.)

  • `C-c C-f' Run until the completion of the current function, and then stop. (The gdb finish command.)

Again, you may wish to bind these to shorter key sequences (such as M-s, M-n, and so on).

Another interesting command is C-x C-a C-p, which will (within the source buffer) take the C expression around point and pass it to gdb's print command, which evaluates the expression and prints its value. This is very handy way to examine variables, data structures, and so forth within the debugger. You can even use this command to call functions and print the return value, if you're executing the debugged program within gdb.

For example, placing point on the line

printf("Last value is %d\n",data[19]);
and pressing C-x C-a C-p will cause the following to be printed in the
gdb buffer:
(gdb)
$1 = 16

In this case, data[19] is 0, because we haven't executed the calculation loop yet. Nevertheless, we can call functions within the program (or, in fact, any arbitrary function, using the print command manually) and examine the return value.

Emacs also allows you to define your own functions for interacting with the debugger. For example, we might want to move the point to a line of code, and run the gdb until function, which will cause execution to continue until that line is reached.

This is accomplished with the gud-def function. For example, we can use (gud-def my-until-line "until %f:%l""\C-u" "Run until current line.")

This will define the function my-until-line which sends the string

until %f:%l

to the gdb process, where %f is replaced with the current source filename, and %l is replaced with the current line number. The new function will be bound to the key sequence C-x C-a C-u (in the source buffer), and C-c C-u (in the gdb buffer). The final argument is the documentation string for the command, printed using describe-function.

Now, we can place point on a line of code in the source buffer, type C-x C-a C-u, and execution will continue until that line of code is reached.

We can customize interactions with the debugger in another way. For example, gdb lacks the inherent ability to automatically step along code, allowing us to monitor the execution of the program without interruption. A similar effect can be achieved by using the step command many times in succession, but we'd like Emacs to automate the process for us.

This can be accomplished using the following function:

(defun gdb-step-forever (arg)
  (interactive "NTime between steps: ")
  (while -t
    (progn
      (sit-for arg)
      (gud-step 1))))

Running this function as M-x gdb-step-forever will prompt us for the amount of time to sleep between steps, in seconds. (This need not be an integral number of seconds—you can specify real values such as 0.5.) The function will then pause for the given amount of time, run gud-step, and repeat, ad infinitum. To interrupt the function, you can use the Emacs quit key, C-g.

A more general extrapolation of this idea would allow us to run a “hook” function between steps, which would allow us to print the values of variables, and so on.

Note that the above function isn't very intelligent—if it runs into a breakpoint, or the program ceases execution for some reason, it will continue to loop naively. In these cases, you can simply interrupt the function by hand.

Given the above tour, you should be ready to tackle the other programming features that Emacs provides—including version control, customized indentation styles, and so forth. Perhaps a future issue of Linux Journal will cover these aspects of Emacs.

I welcome all suggestions, comments, and corrections for the material presented here. Please feel free to send correspondence to the author, c/o Linux Journal, or via electronic mail at mdw@sunsite.unc.edu .

Happy hacking!

Matt Welsh (mdw@sunsite.unc.edu) is a programmer at the Cornell University Robotics and Vision Laboratory.He is a writer and programmer who uses vi almost exclusively—perhaps more by accident than design. He spends his free time homebrewing virtual beer and playing the blues.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Re: Tutorial: Emacs for Programmers

Anonymous's picture

Several small flaws in the article. The local emacs variables need to be at the end of the source. Or at least for Xemacs they do, they didn't work when in the middle of my code.

/* Local Variables: */

/* mode: C */

/* compile-command: "cd ..; make -k" */

/* End: */

The above works for my projects where the source lives 1 directory below the Makefile.

The compile key bindings are missing some backslashes before the C

;; Save and Compile

;; Uses the compile-command variable which can be set in the source

(defun my-save-and-compile ()

(interactive "")

(save-buffer 0)

(compile compile-command)

)

(define-key c-mode-map "C-cC-c" 'my-save-and-compile)

The above uses the compile-command set by the source. If none has been set then make -k is the system default.

Brian Lane(too busy to find my login)

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState