ARSC HPC Users' Newsletter 352, November 10, 2006

Using gdb to Debug Running Programs

There are a lot of methods you can use to debug programs. Not so long ago I used to make use of tried and true print statements when trying to track down bugs, but this is a generally a poor debugging method for several reasons.

  • you have to change the code you are trying to fix.
  • You have to recompile the code each time you change something.
  • When you finally find a bug, you have to undo everything you did.

Now-a-days I almost always use a debugger first. Debuggers work particularly well when:

  • the program is crashing for an unknown reason, especially if it's generating a signal such as a segmentation fault.
  • the program seems to be caught in an endless loop or has deadlocked
  • when you have a core file.

In this article, we'll look at debugging already-running processes with gdb.

Connecting to Running Processes:

Assuming you have compiled the executable with debugging information enabled and you have a shell open to the machine where the process is running (which may not be the case for jobs run through a queuing system), you can use gdb to attach to a running process.

If the program is already running but seems to be off in never-never land, you can do the following to attach to the process:

  1. First, find the process id for the program (here the program is called demo)

       nelchina 1% ps -elf 
     grep demo
       0 R bahls    27869 22154 93  85   0 -   582 ... ./demo
       0 S bahls    27871 22154  0  76   0 -   927 ... grep demo

    The process id is 27869.

  2. Next, start gdb, passing it the executable name and the process id. This will attach gdb to the process and stop the process at the current line of execution.

       nelchina 2% gdb ./demo 27857
       GNU gdb 6.4
       Copyright 2005 Free Software Foundation, Inc.
       GDB is free software, covered by the GNU General Public License, and you are
       welcome to change it and/or 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.
       This GDB was configured as "x86_64-suse-linux"...Using host libthread_db library "/lib64/tls/".
       Attaching to program: /import/home/u1/uaf/bahls/gdb_demo/demo, process 27869
       Reading symbols from /lib64/tls/
       Loaded symbols for /lib64/tls/
       Reading symbols from /lib64/
       Loaded symbols for /lib64/
       0x00000000004004a8 in main () at demo.c:11
       11              cnt++;

    Given this trivial example, gdb has already pointed me to line 11 in "demo.c," the area where the problem is located.

  3. For realistic examples, a stack trace, which prints the stack of subroutine calls which brought us to the currently executing line, is often helpful. The "where" command will display a stack trace:

       (gdb) where
       #0  0x00000000004004a8 in main () at demo.c:11
  4. The "list" command shows a few lines of source code context for the currently executing line.

       (gdb) list
       6           int cnt=0;
       8           while ( cnt < 20 )
       9           {
       10              int cnt=0; 
       11              cnt++;
       12          }
       15          return 0;
  5. As with programs started from gdb on the command line, you can step through the code, one line at a time, and print variables after attaching to the process:

       (gdb) print cnt
       $1 = 0
       (gdb) s
       8           while ( cnt < 20 )
       (gdb) s
       10              int cnt=0; 

    In this interaction, "s" typed at the "(gdb)" prompt is an abbreviation for "step," and "print" dumps the value of the variable "cnt".

    For assistance, you can always type "help" at the "(gdb)" prompt to get a listing of help topics, or "help <cmd>" (e.g. "help list") to get more information on a particular command.

These techniques, including attaching to a process, can also be used on MPI programs, so long as you have shell access to the node where the MPI tasks are running. In a later issue, we'll take a look at using gdb with MPI programs.


The Free Software Foundation provides documentation for gdb here:

Java for Fortran Programmers: Part VI

[[ Thanks to Lee Higbie of ARSC for this tutorial. ]]

Please let me know your interest in this series of articles, it will help to ensure and guide future chapters.

The topic today is:

  • Interfacing Java and Fortran Programs (continued)

In this chapter I will show you how to open and read files. The compilation GUI could be created in many ways, but a table driven system is the one that seems most sensible to me. In this second step toward creating the compilation GUI, I will read a file that lists the names of the supported compilers/systems then reads in the information about each of them. In the process we will also look at Vectors and Arrays. You can download the Java code and sample files for this example and the earlier ones from .

(Download all the files, create a subdirectory higbie, and move the file into the subdirectory higbie. In the main directory type "javac *java" to compile the Java codes. With the files that are there now, you will then have four programs: HelloWorld, HelloBill, CompilerGUIa and CompilerGUIc. The other files are used by CompilerGUIc, as will be explained in this chapter.)

The first important difference from Chapter 5's code is in the main method starting at line 54:

   File [] compileStmntFiles;
   Vector <FileReader> compileStmntNames = new Vector <FileReader>();

Java has two arrayed types of data: Vector and Array. Arrays have subscripts in square brackets and look and behave like subscripted data in other languages. The first line above declares an Array of File objects called compileStmntFiles. At this time the size of the Array has not been declared; only the name is known. Notice that the compiler, javac, checks operations on compileStmntFiles[<anyVal>] to be sure they are allowed on a File object.

Vectors are accessed with methods such as elementAt(i) and addElement. They can grow or shrink as needed. Because the notation is cumbersome, it is common to build a Vector then convert it to an Array once the size is known. That is not done here. The type in angle brackets tells the compiler that all the objects in the Vector are FileReaders, and it will verify that operations performed on the elements of compileStmntNames are legal.

(If you have an old Java compiler, older than Java 1.5, this angle bracket notation will not work. I recommend that you upgrade, but if that is difficult, you can leave them out and the code will work with only one modification. When you extract an element from compileStmntNames, you will have to cast it to FileReader type:

    FileReader fr1 = (FileReader) compileStmntNames.elementAt(3);
for example.)

The constructor for the Vector of FileReaders has no parameters, the parens at the end of the line are empty, so compileStmntNames will start with ten element slots, and will double the number of available locations whenever space runs out. There are two other constructors for Vectors where you specify the initial available size and the incremental size.

The next new feature of ComplerGUIc starts at line 65:

65    try {          //  read in the compilerNames file
66      BufferedReader fileNamesRdr = new BufferedReader(
67                         new FileReader("CompileStmntNames"));
68       for (int i = 0; true; i++) {
69          String st = fileNamesRdr.readLine();
70          if (st == null) { break; }
71          compileStmntNames.addElement(new FileReader(st));
72          deb.print("file", i +" "+ st +".");
73       }
74       } catch (IOException ioe) {         // end try
75          System.out.println("Problem with names of files: "+ ioe);
76          System.exit(4);
77    }        // end try/catch block

I mentioned long ago that Java forces users to deal with exceptions. Here is an example of this. Any method that can create an exception must declare that it throws an exception and any place where the method is used, the using code must be prepared to catch the exception using a try...catch block.

In this code fragment, the constructor for FileReader (line 67) and the readLine (line 69) methods (the constructor is a method) both declare that they throw an exception. FileReader actually throws a FileNotFoundException, which is a type of IOException. Because I'm lazy I only have one try...catch block. If I were being careful, writing production code, for example, I would have two separate ones.

If you have not done much Java coding, you'll also want to note two features of statement in line 70. First, the normal way to check for string, or any object, equality, is to use the equals() method. The == symbol actually compares the underlying pointer, which is much more restrictive than checking that the strings having identical content. Here, if the line is blank, st will be null so the == test works. Second, the break statement interrupts the flow of control, transferring to just past the end of the current for loop. The break is also used in switch (Java's case) statements.

In line 71 I add the line to the Vector of compiler names and, in line 72, call the print method of the Debug object deb. If you add the string "file" to debargs in line 27 of the original program, this statement will print some information about the I/O. (static String [] debargs = {"1", "file"};)

Finally, this gets us down to the important part of the try...catch block. If an I/O error occurs, an IOException will be thrown, either from line 67 or 69) and control will transfer to the catch block. There I print out some diagnostic info and terminate the application.

This is enough foundation to start making an interesting GUI for generating compile statements. Next chapter I'll put it together and start creating the code as promised at the outset.

Santa Letters, Postmarked "North Pole"

Uncles, Aunts, Parents, Teachers, and other friends of kids: here's an offer from the ARSC HPC Newsletter Crew.

North Pole, Alaska is just down the road. If you'd like a card, postmarked "North Pole," delivered to someone, seal it in a stamped, addressed envelope, and instead of mailing it from your local post office, enclose it in a larger envelope and send this to us. On or about December 11th we'll mail it, and it should arrive with the special North Pole Christmas postmark.

Send to:

Tom Baring and Don Bahls Arctic Region Supercomputing Center University of Alaska Fairbanks P.O. Box 756020 Fairbanks AK 99775-6020

Plan extra time for mail to/from Alaska... If you post these to us on or before Dec. 4th, there should be no problem.

Happy Thanksgiving, Everyone

We'll see you in December.

Quick-Tip Q & A

A:[[ I'd like the ESC key on my keyboard to be larger.  What can I do?

  # I expected someone to say, "just stick a wad of gum on it," but 
  # instead we actually got a helpful suggestion.
  # Thanks to Jed Brown:
  Remap the Caps_Lock key to be Escape.  It is larger and easier to 
  reach.  Even though I am no longer a Vim user, I use the Escape key 
  much more than Caps_Lock.

  Stranger still, I map my right Alt key to Control so I can get it 
  with my thumb---Emacs chording bliss!

Q: I really like command completion in bash and tcsh.  Is there a 
   way to get the shell to do command completion for hostnames? It
   would be nice not to have to type the whole hostname when I ssh
   to another machine.  E.g., I'd like:

     ssh iceb[TAB]

   To be expanded to: 


   Yes, I am really this lazy.

[[ Answers, Questions, and Tips Graciously Accepted ]]

Current Editors:
Ed Kornkven ARSC HPC Specialist ph: 907-450-8669
Kate Hedstrom ARSC Oceanographic Specialist ph: 907-450-8678
Arctic Region Supercomputing Center
University of Alaska Fairbanks
PO Box 756020
Fairbanks AK 99775-6020
E-mail Subscriptions: Archives:
    Back issues of the ASCII e-mail edition of the ARSC T3D/T3E/HPC Users' Newsletter are available by request. Please contact the editors.
Back to Top