GDB and DDD are commonly known as debuggers for C/C++ programs, but they can be used for development in other languages, as well. Eclipse was originally designed for Java development, but it has plug-ins for many other languages. This chapter will show you how to use this multilanguage capability.
GDB/DDD/Eclipse are not necessarily the "best" debuggers for any particular language. A large number of excellent debugging tools are available for specific languages. What we are saying, though, is that it would be nice to be able use the same debugging interface no matter which language you are writing in, whether it be C, C++, Java, Python, Perl, or other languages/debuggers that these tools can be used with. DDD has been "ported" to all of them.
For example, consider Python. The Python interpreter includes a simple, text-based debugger of its own. Again, a number of excellent Python-specific GUI debuggers and IDEs do exist, but another option is to use DDD as an interface to Python's built-in debugger. This enables you to achieve the convenience of a GUI while still using the interface that you are familiar with from your C/C++ coding (DDD).
How does the multilanguage versatility of these tools come about?
Though GDB was originally created as a debugger for C/C++, the GNU people later offered a Java compiler too, GCJ.
Recall that DDD is not a debugger in its own right, but rather a GUI through which you can issue commands to an underlying debugger. For C and C++, that underlying debugger is typically GDB. However, DDD can be, and often is, used as a frontend for debuggers specific to other languages.
Eclipse is also just a frontend. The plug-ins for various languages give it the power to manage the development and debugging of code in those languages.
In this chapter, we'll give an overview of debugging in Java, Perl, Python, and assembly language with these tools. It should be noted that in each case there are additional features not covered here, and we urge you to explore the details for the language you are using.
As an example, let's consider an application program that manipulates a linked list. Here objects of class Node
represent the nodes in a linked list of numbers, which are maintained in order of ascending key value. The list itself is an object of class LinkedList
. The test program TestLL.java reads in numbers from the command line, builds up a linked list consisting of those numbers in sorted order, and then prints out the sorted list. Here are the source files:
1 // usage: [java] TestLL list_of_test_integers 2 3 // simple example program; reads integers from the command line, 4 // storing them in a linear linked list, maintaining ascending order, 5 // and then prints out the final list to the screen 6 7 public class TestLL 8 { 9 public static void main(String[] Args) { 10 int NumElements = Args.length; 11 LinkedList LL = new LinkedList(); 12 for (int I = 1; I <= NumElements; I++) { 13 int Num; 14 // do C's "atoi()", using parseInt() 15 Num = Integer.parseInt(Args[I-1]); 16 Node NN = new Node(Num); 17 LL.Insert(NN); 18 } 19 System.out.println("final sorted list:"); 20 LL.PrintList(); 21 } 22 }
1 // LinkedList.java, implementing an ordered linked list of integers 2 3 public class LinkedList 4 { 5 public static Node Head = null; 6 7 public LinkedList() { 8 Head = null; 9 } 10 11 // inserts a node N into this list 12 public void Insert(Node N) { 13 if (Head == null) { 14 Head = N; 15 return; 16 } 17 if (N.Value < Head.Value) { 18 N.Next = Head; 19 Head = N; 20 return; 21 } 22 else if (Head.Next == null) { 23 Head.Next = N; 24 return; 25 } 26 for (Node D = Head; D.Next != null; D = D.Next) { 27 if (N.Value < D.Next.Value) { 28 N.Next = D.Next; 29 D.Next = N; 30 return; 31 } 32 } 33 } 34 35 public static void PrintList() { 36 if (Head == null) return; 37 for (Node D = Head; D != null; D = D.Next) 38 System.out.println(D.Value); 39 } 40 }
Java is ordinarily thought of as an interpreted language, but with GNU's GCJ compiler, you can compile Java source to native machine code. This enables your Java applications to run much faster, and it also means you can use GDB for debugging. (Make sure you have GDB version 5.1 or later.) GDB, either directly or via DDD, is more powerful than JDB, the debugger that comes with the Java Development Kit. For instance, JDB does not allow you to set conditional breakpoints, which is a basic GDB debugging technique, as you have seen. Thus you not only gain by having to learn one fewer debugger, but you also have better functionality.
First compile the application into native machine code:
$ gcj -c -g Node.java $ gcj -c -g LinkedList.java $ gcj -g --main=TestLL TestLL.java Node.o LinkedList.o -o TestLL
These lines are analogous to the usual GCC commands, except for the -main=TestLL
option, which specifies the class whose function main()
is to be the entry point for execution of the program. (We compiled the two source files one at a time. We found this to be necessary in order to ensure that GDB keeps track of the source files correctly.) Running the program on test input gives the following:
$ TestLL 8 5 12 final sorted list: 5 8
Somehow the input 12 disappeared. Let's see how to use GDB to find the bug. Start GDB as usual, and first tell it not to stop or print announcements to the screen when Unix signals are generated by Java's garbage collection operations. Such actions are a nuisance and may interfere with your ability to single-step using GDB.
(gdb) handle SIGPWR nostop noprint Signal Stop Print Pass to program Description SIGPWR No No Yes Power fail/restart (gdb) handle SIGXCPU nostop noprint Signal Stop Print Pass to program Description SIGXCPU No No Yes CPU time limit exceeded
Now, since the first apparent casualty of the bug was the number 12 in the input, let's set a breakpoint at the beginning of the Insert()
method, conditional on the node's key value being equal to 12:
(gdb) b LinkedList.java:13 if N.Value == 12 Breakpoint 1 at 0x8048bb4: file LinkedList.java, line 13.
As an alternative, you might think to try the command
(gdb) b LinkedList.java:Insert if N.Value == 12
However, although this would work later on, at this point, the LinkedList
class has not yet been loaded.
Now run the program in GDB:
(gdb) r 8 5 12 Starting program: /debug/TestLL 8 5 12 [Thread debugging using libthread_db enabled] [New Thread -1208596160 (LWP 12846)] [New Thread -1210696800 (LWP 12876)] [Switching to Thread -1208596160 (LWP 12846)] Breakpoint 1, LinkedList.Insert(Node) (this=@47da8, N=@11a610) at LinkedList.java:13 13 if (Head == null) { Current language: auto; currently java
Recalling the Principle of Confirmation, let's confirm that the value about to be inserted is 12:
(gdb) p N.Value $1 = 12
Now let's step through the code:
(gdb) n 17 if (N.Value < Head.Value) { (gdb) n 22 else if (Head.Next == null) { (gdb) n 26 for (Node D = Head; D.Next != null; D = D.Next) { (gdb) n 27 if (N.Value < D.Next.Value) { (gdb) p D.Next.Value $2 = 8 (gdb) n 26 for (Node D = Head; D.Next != null; D = D.Next) { (gdb) n 12 public void Insert(Node N) { (gdb) n 33 } (gdb) n TestLL.main(java.lang.String[]) (Args=@ab480) at TestLL.java:12 12 for (int I = 1; I <= NumElements; I++) {
Hmm, that's not good. We went through all of Insert()
without inserting the 12.
Looking a bit more closely, you'll see that in the loop starting at line 26 of LinkedList.java, we compared the value to be inserted, 12, to the two values currently in the list, 5 and 8, and found that in both cases the new value was larger. That in fact is where the error lies. We didn't deal with the case in which the value to be inserted is larger than all of the values already in the list. We need to add code after line 31 to handle that situation:
else if (D.Next.Next == null) { D.Next.Next = N; return; }
After making that correction, you'll find that the program works as it should.
These steps are much easier and more pleasant if you use DDD as your interface to GDB (again assuming the source code is compiled using GCJ). Start up DDD as usual:
$ ddd TestLL
(Ignore the error message about a temporary file.)
The source code does not immediately appear in DDD's Source Text window, so you can begin by using DDD's Console:
(gdb) handle SIGPWR nostop noprint (gdb) handle SIGXCPU nostop noprint (gdb) b LinkedList.java:13 Breakpoint 1 at 0x8048b80: file LinkedList.java, line 13. (gdb) cond 1 N.Value == 12 (gdb) r 8 5 12
At this point the source code appears, and you are at the breakpoint. You can then proceed with DDD operations as usual.
DDD can be used directly with the Java Development Kit's JDB debugger. The command
$ ddd -jdb TestLL.java
will start DDD, which then invokes JDB.
However, we have found that it then becomes quite cumbersome, so we do not recommend this usage of DDD.
If you originally downloaded and installed a version of Eclipse that is tailored to C/C++ development, you'll need to get the JDT (Java Development Tools) plug-in.
The basic operations are as we described before for C/C++, but note the following:
When creating your project, make sure to choose Java Project. Also, we recommend that you check Use Project Folder As Root for Sources and Classes.
Use the Package Explorer view for your navigator.
Source files will be compiled to .class as soon as they are saved (or imported).
When you create a run dialog, right-click Java Application and select New. In the space labeled Main Class, fill in the class in which main()
is defined.
When you create a debug dialog, check Stop in Main.
In debug runs, Eclipse will stop before main()
. Just click the Resume button to continue.
Figure 8-1 shows a typical Java debugging scene in Eclipse. Note that as with C/C++, in the Variables view we have displayed the values of the node N
by pointing the triangle next to N
downward.