...And so you have just spent the last year working with the white NeXTs and have learned the ins and outs of playing with Gnu's debugger (gdb). And now you've been forced to start working on one of the other machines.-- and GDB doesn't work.
Faced with the prospect of having to write forty printfs into your program telling what your variables' values are at crucial times, you begin to wonder if there is any alternative. And actually, there is one; it's called DBX.
For Carleton's purposes, DBX is a universal debugger for our UNIX systems. It can be accessed from all of the Indys and the NeXTs; that alone is a helpful feature of DBX. However, it is command-based, so you will be inputing commands via keyboard rather than mouse.
To make use of DBX, you need to insert the -g flag for debugging purposes into your compilation line. Once compiled you then type:
blue 1% dbx [executable]
DBX is not the easiest of programs to deal with. Do not expect to pick up the syntax right away or use it like a pro. The following tutorial should give you some basic skils to begin to work with dbx. Use the help files within dbx once you have specific questions. In our case, we are going to use a simple program to display most of the basic features of dbx. Read along; it may actually help if you copy the program and learn by doing:
/* debug.c */ #includeThis program obviously has an infinite loop problem. But let's say that we were at a loss of what to do about it, and that we wanted the computer to stop at 10. We first start up dbx:void printit(int j) { printf("%i\n", j); } void main() { int i=0; for (i=0; i >= 0; i++) { printit(i); } }
blue 1% dbx debug
dbx version 3.19 Nov 3 1994 19:59:46
Executable /Net/deimos/great-space/math/johnsond/debug
(dbx)
(dbx) help most_usedgives us a listing of the commands that we will, in all likelihood, use.
(dbx) run 0 1 2 3 4 5 6 7 8 9 10 ...Obviously, we wish to alter the program so that the computer stops at 10. To stop an infinite loop, press [Ctrl-C] If debug took arguments, you would place them after 'run'. This command can be shortened by simply typing 'r' at the dbx prompt; this will also activate any arguments that you called the last time 'run' was used.
First, we place a stop in the main after the initialization of the variables.
(dbx) list
>* 12 {
13 int i=0;
14 for (i=0; i >= 00; i++)
15 {
16 printit(i);
17 }
18 }
(dbx)
By calling 'list', the computer proceeds to print out the main with
its line numbers appended to it. By adding the name of a procedure/function
as an argument right after 'list', dbx will list the code starting
at that particular procedure.
We know the initialization happens at line 13, so we put a stop right after
the initialization.
(dbx) stop at 14
Process 701: [3] stop at "/Net/deimos/great-space/math/johnsond/debug.c":14
(dbx)
We now run the program:
(dbx) run
Process 701 (debug) started
[3] Process 701 (debug) stopped at [main:14 ,0x400a5c]
14 for (i=0; i >= 00; i++)
(dbx)
We now want to trace the variable i:
(dbx) trace i
Process 701: [4] trace i in main
(dbx)
Just for kicks, it might be nice to also trace when a certain procedure
(i.e. printit) is being called:
(dbx) trace printit
Process 701: [5] trace printit
(dbx)
It is also possible to trace a variable within a procedure; in fact, this
is the only way that you can see variable changes of a non-main variable:
(dbx) trace j in printit
Process 701: [6] trace j in printit
(dbx)
Now that we have put a trace on almost every concievable thing that we
can trace and have placed a stop in the middle of the program, let's quickly
examine what stops, traces, and traps have been set:
(dbx) status
Process 701: [3] stop at "/Net/deimos/great-space/math/johnsond/debug.c":14
Process 701: [4] trace i in main
Process 701: [5] trace printit
Process 701: [6] trace j in printit
(dbx)
Notice how each seperate stop or trace has a number that pertains to it inside
the brackets. Now, let's continue through the program to see what's wrong
with debug.c...
(dbx) step
Process 701 (debug) stopped at [main:16 ,0x400a60]
16 printit(i);
(dbx)
Single-stepping can be done with either the command 'step' or
's'. It is also possible to step a certain number of times at once
by entering 'step [# of times]':
(dbx) step 2
[5] calling printit(j = 0) from function main
0
Process 701 (debug) stopped at [printit:9 ,0x400a28]
9 }
(dbx)
We have moved two steps through the program. Trace [5] has been sprung,
which checks for when the procedure 'printit' has called, and so the computer
outputs which function called it.
Instead of single stepping, it is also possible to simply continue until either a certain procedure is called or until a certain line number is come to. First, let us simply let the computer continue the program for a time:
(dbx) cont
[5] printit returning from printit
[4] i changed before [main: line 14]:
new value = 1;
[5] calling printit(j = 1) from function main
1
[5] printit returning from printit
[4] i changed before [main: line 14]:
old value = 1;
new value = 2;
[5] calling printit(j = 2) from function main
2
[5] printit returning from printit
[4] i changed before [main: line 14]:
old value = 2;
new value = 3;
[5] calling printit(j = 3) from function main
3
...
[5] printit returning from printit
[4] i changed before [main: line 14]:
old value = 7
new value = 8;
[5] calling printit(j = 8) from function main
8
More (n if no)?n
(dbx)
One would think that a command such as cont in this situation would
go ad nauseum. In fact, it simply halts after a time and asks whether to
continue or not. To continue until a procedure is next called, you must
first exit that procedure and then call cont in [proc], unless there
is a call to the procedure within itself:
(dbx) cont in printit
[5] calling printit(j = 0) from function main
8 printf("%i\n", j);
(dbx)
There is also a 'cont' command that allows you to continue until
a certain line number:
(dbx) cont to 16
16 printit(i);
(dbx)
Finally, after using all of these tools, one will finally be able to find
the problem in the code for 'debug.c'. To finish the job, type:
(dbx) quit
Process 701 (debug) terminated
blue 2%