Chapter 8. USING GDB/DDD/ECLIPSE FOR OTHER LANGUAGES

USING GDB/DDD/ECLIPSE FOR OTHER LANGUAGES

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?

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:

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.