4.3BSD/usr/doc/ps1/11.dbx/dbx.ms

Compare this file to the similar file:
Show the results in this format:

.\"	@(#)dbx.ms	6.4 (Berkeley) 5/10/86
.\"
.\" modified by mark seiden in cosmetic ways.
.\" examples VAXinated by Kevin Dunlap
.\" dtbl | ditroff -ms
.OH 'Debugging with dbx''PS1:11-%'
.EH 'PS1:11-%''Debugging with dbx'
.de BE
.DS
.ft CW
.ps -1
..
.de EE
.ft P
.ps +1
.DE
..
.de UL
\f(CW\s-1\\$1\fP\s0
..
.TL
Debugging with dbx
.AU
Bill Tuthill
.AI
Sun Microsystems, Inc.
2550 Garcia Avenue
.AU
Kevin J. Dunlap
.AI
Computer Systems Research Group
University of California
Berkeley, CA 94720
.SH
Introduction
.PP
This short paper discusses
.I dbx ,
a symbolic debugger that is vastly superior to
.I adb .
It may be as good as the debuggers you remember from those
non-
.UX 
systems you worked on before.
The advantage of symbolic debuggers is that they allow you
to work with the same names (symbols) as in your source code.
.PP
Like
.I adb ,
.I dbx
is interactive and line-oriented, but
.I dbx
is a source-level rather than an assembly-level debugger.
It allows you to determine where a program crashed,
to view the values of variables and expressions,
to set breakpoints in the code, and to run and trace a program.
Source code may be in C, Fortran, or Pascal.
.PP
Mark Linton wrote
.I dbx
as his master's thesis at UC Berkeley.
Along with Eric Schmidt's Berknet,
.I dbx
is among the most successful master's theses done on UNIX.  Since
.I dbx
required changes to the symbol tables
generated by the various compilers,
you need to compile programs for debugging with the
.I \-g
flag.  For example,
C programs should be compiled as follows:
.DS
% cc \-g \fIprogram\fP.c \-o \fIprogram\fP
.DE
Programs compiled with the
.I \-g
option have good symbol tables,
while programs compiled without
.I \-g
have old-style symbol tables intended for
.I adb .
Stripped programs have no symbol tables at all.
Invoke the debugger as follows, where
.I program
is the pathname of the executable file that dumped core:
.DS
% dbx \fIprogram\fP
.DE
The core image should be in the working directory;
if it isn't, specify its pathname in the argument after the program name.
Among the great advances of
.I dbx
is that it has a help facility; type the
.I help
request to see a list of possible requests.
You can obtain help on any
.I dbx
request by giving its name as an argument to
.I help .
.bp
.SH
Examining Core Dumps
.PP
Much of the time, programmers use
.I dbx
to find out why a program dumped core.
As an example, consider the following program
.I dumpcore.c ,
which dereferences a NULL pointer.
This is a legal operation on VAX/UNIX,
but not on VAX/VMS or on MC68000-based UNIX systems, on one of
which this example was run:
.BE
#include <stdio.h> 
.sp.5
#define LIMIT 5
.sp.5
main()			/* print messages and die */
{
	int i;
.sp.5
	for (i = 1; i <= 10 ; i++) {
		printf("Goodbye world! (%d)\en", i);
		dumpcore(i);
	}
	exit(0);
}
.sp.5
int *ip;
.sp.5
dumpcore(lim) 		/* dereference NULL pointer */
int lim;
{
	if (lim >= LIMIT)
		*ip = lim;
} 
.EE
The program core dumps because of a
segmentation violation or memory fault \(em
on most machines it is illegal to assign to address zero.
Once the program has produced a core dump,
here's how you can find out why the program died:
.DS
%\c
.UL " dbx dumpcore"
dbx version 3.17 of 4/24/86 15:04 (monet.Berkeley.EDU).
Type 'help' for help.
reading symbolic information ...
[using memory image in core]
(dbx)\c
.UL " where"
dumpcore.dumpcore(lim = 5), line 22 in "dumpcore.c"
main(0x1, 0x7fffe904, 0x7fffe90c), line 11 in "dumpcore.c"
.DE
The
.I where
request yields a stack trace.
As you can see, the
.I dumpcore()
routine was called from line 11 of the program, with the argument
.I lim
equal to 5.
You can look at the
.I dumpcore()
procedure by invoking the
.I list
request as follows:
.DS
(dbx)\c
.UL " list dumpcore"
   18   dumpcore(lim)           /* dereference NULL pointer */
   19   int lim;
   20   {
   21           if (lim >= LIMIT)
   22                   *ip = lim;
   23   }
.DE
We immediately suspect that the program's failure had something to do with
.I *ip ,
so we use the
.I print
request to retrieve the value of the pointer and what it points to:
.DS
(dbx)\c
.UL " print *ip"
reference through nil pointer
(dbx)\c
.UL " print ip"
(nil)
.DE
This tells us the program has dereferenced a null pointer.
It is possible to run the program again from inside the debugger.
The first line tells you name of the running program,
and successive lines give output from the program:
.DS
(dbx)\c
.UL " run"
Goodbye world! (1)
Goodbye world! (2)
Goodbye world! (3)
Goodbye world! (4)
Goodbye world! (5)
.sp.5
Bus error in dumpcore.dumpcore at line 22
   22		*ip = lim;
(dbx)\c
.UL " quit"
.DE
In this example the program dies with a Bus error at line 22. 
This method of running the program
does not produce a core dump, but the
.I where
request will still behave properly,
because the debugger is in the same state
as if it had just read the core file.
.SH
Setting Breakpoints
.PP
With
.I dbx
you can set breakpoints before each line of a program,
not just at function and procedure boundaries, as with
.I adb .
The
.I stop
request sets a breakpoint.
After setting a breakpoint, use the
.I run
request to execute the program.  The
.I cont
request continues execution from the current stopping point
until the program finishes or another breakpoint is encountered.  The
.I step
request executes one source statement,
following any function calls.  The
.I next
request executes one source statement,
but does not stop inside any function calls.  The
.I status
request lists active breakpoints, while the
.I delete
request removes them if required.
.PP
The
.I stop
request can take a conditional expression
to avoid needless single-stepping.
We will use a conditional in our example to make things simpler.
Of course you can use
.I print
and
.I list
requests at any time during statement stepping
if you want to print the value of variables
or list lines of source code.
This sample session shows a mixture of requests
as we verify that the program fails when it tries to assign to
.I *ip :
.DS
(dbx)\c
.UL " stop at 10 if (i == 5)"
[1] if i = 5 { stop } at 10
(dbx)\c
.UL " run"
Goodbye world! (1)
Goodbye world! (2)
Goodbye world! (3)
Goodbye world! (4)
[1] stopped in main at line 10
   10                   printf("Goodbye world! (%d)\en", i);
(dbx)\c
.UL " next"
Goodbye world! (5)
stopped in main at line 11
   11                   dumpcore(i);
(dbx)\c
.UL " step"
stopped in dumpcore at line 21
   21           if (lim >= LIMIT)
(dbx)\c
.UL " step"
stopped in dumpcore at line 22
   22                   *ip = lim;
(dbx)\c
.UL " step"
Bus error in dumpcore.dumpcore at line 22
   22		*ip = lim;
.DE
Running the program with breakpoints assures us
that our intuition was correct.
We shouldn't be assigning anything to a null pointer \(em
.I ip
should have been initialized to point at an object of the proper type.
To exit from the debugger, use the
.I quit
request.
.PP
It is possible to set variables from inside
.I dbx .
The previous breakpoint session, for example,
could have gone like this:
.DS
%\c
.UL " dbx dumpcore"
dbx version 3.17 of 4/24/86 15:04 (monet.Berkeley.EDU).
Type 'help' for help.
reading symbolic information ...
[using memory image in core]
(dbx)\c
.UL " stop at 10"
[1] stop at 10
(dbx)\c
.UL " run"
Running: dumpcore 
stopped in main at line 10
   10                   printf("Goodbye world! (%d)\en", i);
(dbx)\c
.UL " assign i = 5"
(dbx)\c
.UL " next"
Goodbye world! (5)
stopped in main at line 11
   11                   dumpcore(i);
(dbx)\c
.UL " next"
Bus error in dumpcore.dumpcore at line 22
   22		*ip = lim;
.DE
It is often useful to assign new values to variables
to draw conclusions about alternative conditions.
We can't fix the bug in this program, however,
because there is no declared variable to which
.I ip
should point.
.SH
Conclusion
.PP
Expressions in
.I dbx
are similar to those in C,
except that there is a distinction between
.I /
(floating-point division) and
.I div
(integer division), as in Pascal.
The table on the following page shows
.I dbx
requests organized by function:
.PP
Like
.I adb ,
.I dbx
can disassemble object code.
It can also examine object files
and print output in various formats; but
.I dbx
requires the proper symbol tables, so
.I adb
is more useful to examine arbitrary binary files.
The most important thing
.I adb
can do that
.I dbx
cannot is to patch binary files \(em
.I dbx
has no write option.
Despite these shortcomings,
.I dbx
is much easier to use than
.I adb ,
so it contributes much more to individual programmer productivity.
.SH
Acknowledgements
.PP
Material presented in this document was first presented in
``C Advisor'', \fIUnix Review 4\fP, 1, pp 78\-85.
The Regents of the University California expresses their
gratitude to Unix Review
for allowing them to reprint this document.
.PP
This document is a good starting point for a more thorough tutorial.
Those with the ambition to expand on this document are encouraged
to contact the Computer Systems Research Group at ``4bsd-ideas@Berkeley.Edu.''
.KF
.TS
center box;
cf s.
.sp.2
\s+2Groups of \&\fIdbx\fP Requests\s-2
.sp.2
_
.T&
l lfI
lp-1fCW l.
	execution and tracing
_
run	execute object file
cont	continue execution from where it stopped
trace	display tracing information at specified place
stop	stop execution at specified place
status	display active \&\fItrace\fP and \&\fIstop\fP requests
delete	delete specific \&\fItrace\fP or \&\fIstop\fP requests
catch	start trapping specified signals
ignore	stop trapping specified signals
step	execute the next source line, stepping into functions
next	execute the next source line, even if it's a function
.T&
l lfI
lp-1fCW l.
_
	displaying data
_
print	print the value of an expression
whatis	print the declaration of a given identifier or type
which	print outer block associated with identifier
whereis	print all symbols matching identifier
assign	set the value of a variable
.T&
l lfI
lp-1fCW l.
_
	function and procedure handling
_
where	display active procedures and functions on stack
down	move down the stack towards stopping point
up	move up the stack towards \&\fImain\fP
call	call the named function or procedure
dump	display names and values of all local variables
.T&
l lfI
lp-1fCW l.
_
	accessing source files and directories
_
edit	invoke an editor on current source file
file	change current source file
func	change the current function or procedure
list	display lines of source code
use	set directory list to search for source files
/.../	search down in file to match regular expression
?...?	search up in file to match regular expression
.T&
l lfI
lp-1fCW l.
_
	miscellaneous commands
_
sh	pass command line to the shell
alias	change \&\fIdbx\fP command name
help	explain commands
source	read commands from external file
quit	exit the debugger
.TE
.KE
.bp