This chapter introduces the features and functions of the Ladebug command interface. It describes how to:
This chapter also presents an example debugging session from the command interface.
For an introduction to the Ladebug window interface, see Chapter 2.
There are several ways to invoke Ladebug from the command interface and bring your program under debugger control. For example, you can invoke Ladebug from the command interface and specify:
Once you have invoked Ladebug, you can bring a process or program under debugger control from the Ladebug prompt by attaching to a process or loading a program. For applications that fork and/or exec, you can also control whether to bring the child process under debugger control.
To bring a program or process under debugger control from the shell, choose the appropriate syntax for invoking Ladebug from among the following:
$ ladebug executable_file
$ ladebug executable_file core_file
$ ladebug -pid process_id executable_file
$ ladebug -k /umunix
$ ladebug -remote
$ ladebug -rn node_or_address [,udp_port]
For information on using the Ladebug prompt from within the window interface, see Section 2.4.
You can bring a program or process under debugger control after invoking Ladebug from the command interface or window interface. From the Ladebug prompt, you can:
(ladebug) load image_file [core_file]
(ladebug) attach process-id image_file
For information about attaching to a process, see Section 7.8.9 and Section 9.13. For information about loading a program, see Section 20.4.
You can enter several commands on a single line by separating the commands with a semicolon (;). The commands are executed in the order in which you enter them. Example 7-1 shows how to enter multiple commands on a single line.
(ladebug) stop in main;run [#1: stop in main ] [1] stopped at [main:4 0x120000a40] 4 for (i=1 ; i<3 ; i++) { (ladebug) where >0 0x120000a40 in main() sample.c:4 (ladebug)
You can enter multiline commands by using a backslash (\) at the end of a line to be continued.
The debugger predefines a set of debugger variables. You can display and modify these variables to alter debugger settings. You can also create new debugger variables to use within other commands, or as placeholders of important information.
All debugger variable names start with a dollar sign ($). The
set
command, used alone, causes the display of all the
debugger variables with their current values. The set
command also lets you set the value of a debugger variable. Using
this command, you can redefine an existing debugger variable or
create a new debugger variable. The syntax for defining a debugger
variable with this command is as follows:
set variable = value
If the value of the variable is a text string, enclose the string
in quotes. Example 7-2 shows how to use
the set
command to display and redefine debugger
variables. In this example, all the predefined variables are
displayed by the set
command. Then the $lang
and $historylines
variables are changed and
displayed. (The $lang
variable determines the language
syntax and visibility rules the debugger uses; the value for the
$lang
variable is a string enclosed in quotation
marks. The $historylines
variable determines the
number of lines listed by the history
command.)
(ladebug) set $ascii = 1 $beep = 1 $catchexecs = 0 $catchforks = 0 $curevent = 0 $curfile = (null) $curline = 0 $curpc = 0 $cursrcline = 0 $curthread = 0 $decints = 0 $editline = 1 $eventecho = 1 $hasmeta = 0 $hexints = 0 $historylines = 20 $indent = 1 $lang = "C" $listwindow = 20 $main = "main" $maxstrlen = 128 $octints = 0 $overloadmenu = 1 $pimode = 0 $prompt = "(ladebug) " $repeatmode = 1 $stackargs = 1 $stepg0 = 0 $stoponattach = 0 $stopparentonfork = 0 $threadlevel = "decthreads" $verbose = 0 (ladebug) set $lang = "C++" (ladebug) set $historylines = 40 (ladebug) print $lang "C++" (ladebug) print $historylines 40 (ladebug)
For more information on these debugger variables, see Part V, Command Reference.
Use the unset
command to delete a debugger variable
that you created, or to return a debugger variable to its default
value. The syntax for the unset
command is as follows:
unset variable
The debugger lets you use abbreviations for frequently used
commands. These abbreviations are called aliases. You
can list all available aliases by entering alias
at
the debugger prompt. To view the definition of a single alias, enter
alias
followed by the alias name.
To delete an alias, enter unalias
followed by the
alias name.
Several aliases are predefined by the debugger. The predefined
aliases substitute one or two letters for whole commands. For
example, l
is the alias for list
and q
is the alias for quit.
A
complete list of predefined aliases is in the description of the
alias
command in Part V, Command
Reference, and is also displayed by the debugger in response to
the alias
command with no arguments.
You can also create your own aliases. The alias
command syntax for creating your own alias is as follows:
alias aliasname "string"
After you define the alias, entering aliasname
is
identical to entering string.
Example 7-3 creates an alias that sets a breakpoint, runs your
program, and performs a stack trace.
(ladebug) alias cs alias cs is not defined (ladebug) alias cs "stop at 5; run; where" (ladebug) alias cs cs stop at 5; run; where (ladebug) cs .oS [#1: stop at "sample.c":5 ] [1] stopped at [main:5 0x120000b1c] 5 f = factorial(i); >0 0x120000b1c in main() sample.c:5 (ladebug)
Aliases may also contain parameters. In Example 7-4, the alias defined in Example 7-3 is modified to specify the breakpoint's line number
when you enter the abbreviated alias
command.
(ladebug) alias cs(x) "stop at x; run; where" (ladebug) alias cs cs(x) stop at x; run; where (ladebug) cs(5) [#1: stop at "sample.c":5 ] [1] stopped at [main:5 0x120000b1c] 5 f = factorial(i); >0 0x120000b1c in main() sample.c:5 (ladebug)
Aliases may have multiple parameters. The alias
command syntax for creating an alias with more than one parameter
is as follows:
alias aliasname (arg1, arg2, [, . . . ]) "string"
You can nest aliases. You can define one alias and use that alias in the definition of another alias. In Example 7-5, such an alias is defined and then used in the definition of another alias.
(ladebug) alias begin "bp main; run" (ladebug) alias sp(x,v) ""begin; stop at x; p v"" (ladebug) alias sp sp(x, v) begin; stop at x; print v (ladebug) sp(10,i) [#4: stop in main ] [4] stopped at [main:4 0x120001180] 4 for (i=1 ; i<=3 ; i++) { 0 (ladebug)
Your definition of an alias can include a quoted string. See the
alias
command in Part V, Command
Reference for an example.
The debugger maintains a list of the commands you enter. Using an
abbreviated command sequence, you can reenter a command without
retyping the entire command (a history feature). Pressing the
Return key at the debugger prompt repeats the last command, provided
the $repeatmode
variable is set to 1, which is the
default. Entering two exclamation points (!!) at the debugger prompt
also repeats the last command (regardless of the setting of the
$repeatmode
variable).
You can examine the list of recently entered commands by entering
the history
command at the debugger prompt. The last
command entered is at the bottom of the numbered list. The number of
commands listed by the history
command is determined
by the value of the $historylines
debugger variable.
To enter a command on the list, type an exclamation point followed
by the number of the command on the history list. You can also
specify a command by indicating how recently the command was last
entered; for example, !-3
reenters the third-to-last
command you entered.
You can also reenter a command by entering an exclamation point
followed by the beginning of the command string. For example, to
reenter the command deactivate 3,
enter the command
!dea.
Example 7-6 uses the
history
mechanism to reenter commands. You can also
use the arrow keys to reenter commands if $editline
is
set to 1, which is the default.
(ladebug) stop in main [#1: stop in main ] (ladebug) run [1] stopped at [main:4 0x120001180] 4 for (i=1 ; i<3 ; i++) { (ladebug) next stopped at [main:5 0x120001188] 5 f = factorial(i); (ladebug) print i 1 (ladebug) next stopped at [main:6 0x1200011a0] 6 printf("%d! = %d\en",i,f); (ladebug) print f 1 (ladebug) print factorial(f) 1 (ladebug) delete all (ladebug) stop in factorial [#2: stop in factorial ] (ladebug) rerun [2] stopped at [factorial:13 0x120001224] 13 if (i<=1) (ladebug) step stopped at [factorial:14 0x120001230] 14 return (1); (ladebug) <Return> stopped at [factorial:17 0x120001264] 17 } (ladebug) <Return> stopped at [main:5 0x120001194] 5 f = factorial(i); (ladebug) print f 0 (ladebug) list $curline - 5: 10 1 #include <stdio.h> 2 main() { 3 int i,f; 4 for (i=1 ; i<3 ; i++) { > 5 f = factorial(i); 6 printf("%d! = %d\en",i,f); 7 fflush(stdout); 8 } 9 } 10 factorial(i) (ladebug) cont 1! = 1 [2] stopped at [factorial:13 0x120001224] 13 if (i<=1) (ladebug) where >0 0x120001224 in factorial(i=2) sample.c:13 #1 0x120001194 in main() sample.c:5 (ladebug) cont [2] stopped at [factorial:13 0x120001224] 13 if (i<=1) (ladebug) where >0 0x120001224 in factorial(i=1) sample.c:13 #1 0x12000124c in factorial(i=2) sample.c:16 #2 0x120001194 in main() sample.c:5 (ladebug) history 10: print f 11: print factorial(f) 12: delete all 13: stop in factorial 14: rerun 15: step 16: step 17: step 18: print f 19: list $curline-5:10 20: cont 21: where 22: cont 23: where 24: history (ladebug) !12 delete all
The sh
command allows you to execute Bourne shell
commands without exiting the debugger. The syntax for the sh
command is as follows:
sh command
The command argument is a valid operating system command expression.
Do not enclose the command in quotes, even if it consists of multiple words separated by spaces.
After the command finishes, a debugger prompt appears and you can continue with your debugging session.
Example 7-7 uses the shell command sh
to list information about a file.
(ladebug) sh ls -l sample.c -rw-r----- 1 Ladebug 259 May 15 13:08 sample.c (ladebug)
In Example 7-8 the grep
shell
command displays the lines containing PROGRAM
in the
Fortran source file data2.f90.
(ladebug) sh grep PROGRAM data2.f90 PROGRAM DATA END PROGRAM DATA
You can also spawn
the Bourne shell from the debugger
by issuing:
(ladebug) sh sh
Ladebug supports simple emacs
style bindings for CTRL
keys and arrow keys to edit a command line, as follows:
CTRL-A | Move to the beginning of the line. |
CTRL-E
| Move to the end of the line. |
CTRL-D | Delete a character in place. |
CTRL-K |
Kill (cut) from the cursor to the end of line, into the cut buffer. |
CTRL-Y |
Yank (paste) from the cut buffer, at the position of the cursor. |
CTRL-P or up arrow
| Access items in the history, backward. |
CTRL-N or down arrow | Access items in the history, forward. |
CTRL-F or right arrow | Move the cursor to the right. |
CTRL-
B or left arrow | Move the cursor to the left. |
The debugger variable $editline
enables these key
bindings. By default, this variable is set to 1, and they are
enabled. (For backward compatibility, you can set $editline
to 0. The $editline
is also set to 0 when you
use emacs
with Ladebug.)
The debugger variable $beep
controls whether a beep
sounds when a user tries to perform an illegal action; for example,
moving the cursor past the end of a line, or "yanking" from an empty
cut buffer. By default, the $beep
variable is set to
1, enabling the beep to sound.
For more information on using the set
and
unset
commands with debugger variables, see Section 7.3.
Command-line editing features that are not supported in the
current release include multiple editing modes (emacs
and vi
), multi-line editing, and binding arbitrary
keys to actions.
This section describes the steps necessary to compile and debug a
short C program. If you are new to source-level debugging, edit a
file called sample.c,
type in the sample program, and
follow the instructions for the sample debugging session.
The sample debugging session shows you how to:
For information about basic debugging techniques, see Section 1.3. For information about the Ladebug commands used in this sample session, see Part V, Command Reference.
The sample program in Example 7-9 uses
only C constructs. The program is intended to print the factorials
of 1, 2,
and 3
and then exit.
#include <stdio.h> main() { int i,f; for (i=1 ; i<3 ; i++) { f = factorial(i); printf("%d! = %d\n",i,f); fflush(stdout); } } factorial(i) int i; { if (i<=1) return (1); else return (i * factorial(i-1) ); }
Example 7-10 demonstrates the steps
required to compile, link, and execute the sample program. These
instructions assume that the sample program is named sample.c
and that you are using the C compiler cc
to
build your executable file:
-g
option on the compiler command
line to instruct the compiler to include in the executable file
symbol-table information useful to the debugger.
-o
option to instruct the
compiler to place the executable image in a file named
sample.
% cc -g sample.c -o sample % sample 1! = 1 2! = 2
Something is wrong; the program compiles and runs without an error
message but does not print the factorial of 3.
You can
use the Ladebug debugger to determine the problem.
Example 7-11 demonstrates how to invoke
the debugger on the program by using the ladebug
command. When invoked, the debugger displays a startup banner and
some information about the program being debugged. The debugger
prompt, (ladebug)
, is displayed when the debugger is
waiting for your next command.
(%) ladebug sample Welcome to the Ladebug Debugger Version 4.0 ------------------ object file name: sample Reading symbolic information ...done (ladebug)
At this point, you can examine the program being debugged, set breakpoints or tracepoints, or run the program under debugger control.
To look at the source code lines, enter the list
command using the following syntax:
list line_number
The debugger displays the compiler-generated number for each line in
the target program. Many debugger commands (including the list
command) and messages refer to these line numbers. Example 7-12 shows how to use the list
command to view the program source file.
(ladebug) list 1 1 #include <stdio.h> 2 main() { 3 int i,f; 4 for (i=1 ; i<3 ; i++) { 5 f = factorial(i); 6 printf("%d! = %d\n",i,f); 7 fflush(stdout); 8 } 9 } 10 factorial(i) 11 int i; 12 { 13 if (i<=1) 14 return (1); 15 else 16 return (i * factorial(i-1) ); 17 } (ladebug)
By default, the debugger lists 20 lines of source code at a time.
The list 1
command in this example specifies that the
listing is to begin with the first line of the program. The program
is less than 20 lines, so a listing beginning with line 1 displays
the entire program.
If you have a rough idea of where the error is occurring in your
program, you can set a breakpoint and run the program under debugger
control. A breakpoint set at a line number causes the debugger to
suspend program execution each time that line is encountered. To set
the breakpoint at a particular line number, enter the stop at
command using the following syntax:
stop at line_number
You can also set a breakpoint in a function so that the debugger
suspends program execution when it enters the specified function.
Using the following syntax, the stop in
command lets
you set a breakpoint at a function:
stop in function_name
According to the sample program listing in Example 7-12, the call to the function factorial
is on line 5. The breakpoint command in Example 7-13 shows how to set a breakpoint on line 4.
(ladebug) stop at 4 [#1: stop at "sample.c":4 ] (ladebug)
The debugger confirms the breakpoint by assigning the breakpoint
a reference number and reiterating the breakpoint command. In this
example, the reference number is 1.
Use the run
or rerun
command to instruct
the debugger to execute your program. During execution, you can
examine your program's variables, trace the stack, or step through
the program line by line.
When the debugger reaches a breakpoint, the debugger displays the breakpoint that suspended execution and the line of code that will be executed next, if normal program execution is continued. In Example 7-14, program execution is suspended because of the breakpoint. The next line of code that will be executed if line-by-line execution is continued is line 4.
(ladebug) run [1] stopped at [main:4 0x120001180] 4 for (i=1 ; i<3 ; i++) { (ladebug)
Use the print
command to examine the program state.
The syntax for the print
command is as follows:
print expression
The expression argument is any expression containing one or more variables, constants, and operators that is valid in the current context. In Example 7-15 a variable is evaluated and the debugger prints the result.
(ladebug) print i 0 (ladebug)
The output shows that the value of the variable i is 0.
When program execution is suspended, you can continue execution on
a line-by-line basis by using the step
and next
commands, or you can inspect the program state. After a
line executes, the debugger prompt returns. Pressing the Return key
repeats the previous command.
The step
command executes the next line of code in
the program. If that line of code calls another function, and if
there is debugging information about that function available to
the debugger, the step
command executes the called
function line by line. This is called stepping into a function. If
debugging information is not available to the debugger, as is the
case with the printf
and fflush
library
routines, the debugger will execute the called function and stop at
the next line of code in the function that initiated the call.
The next
command also executes the next line of code
in the program. If that line of code calls another function, the
next
command executes that function and stops at the
next line of code in the function that initiated the call. This
is called stepping over a function. The next
command
steps over called functions; the step
command
steps into called functions.
main
i
changes to
1
factorial
After the debugger executes the function factorial,
the
debugger returns to the function main
and steps over
the library routines printf
and fflush.
(ladebug) step stopped at [main:5 0x120000b1c] 5 f = factorial(i); (ladebug) print i 1 (ladebug) next stopped at [main:6 0x120000b38] 6 printf("%d! = %d\n",i,f); (ladebug) step 1! = 1 stopped at [main:7 0x120000a7c] 7 fflush(stdout); (ladebug) step stopped at [main:8 0x120000a48] 8 } (ladebug)
The stack trace lets you follow the dynamic call chain from function
to function. The where
command displays the stack
trace. The stack trace displays the most recently called function
on the top of the stack. Each function is followed by its calling
function, until all active functions are displayed.
Each function on the stack is placed on a separate line, and is associated with a number that corresponds to an activation level relative to the top of the stack. The most recently called function is on level 0, at the top of the stack.
Example 7-17 continues to step through
the sample program. As the program computes 2!,
control
passes from main
to factorial
and
back in a predictable fashion. A stack trace taken while stepping
through the function factorial()
for the second time
(when i
equals 2
) contains an entry
for function main
and two entries for the recursive
function factorial.
(ladebug) step stopped at [factorial:13 0x120001224] 13 if (i<=1) (ladebug) <Return> stopped at [factorial:16 0x12000123c] 16 return (i * factorial(i-1) ); (ladebug) <Return> stopped at [factorial:13 0x120001224] 13 if (i<=1) (ladebug) where >0 0x120001224 in factorial(i=1) sample.c:13 #1 0x12000124c in factorial(i=2) sample.c:16 #2 0x120001194 in main() sample.c:5 (ladebug) step stopped at [factorial:14 0x120001230] 14 return (1); (ladebug) <Return> stopped at [factorial:17 0x120001264] 17 } (ladebug) <Return> stopped at [factorial:16 0x12000123c] 16 return (i * factorial(i-1) ); (ladebug) <Return> stopped at [factorial:17 0x120001264] 17 } (ladebug) <Return> stopped at [main:5 0x120001194] 5 f = factorial(i); (ladebug) <Return> stopped at [main:6 0x1200011a0] 6 printf("%d! = %d\n",i,f); (ladebug) <Return> 2! = 2 stopped at [main:7 0x1200011c0] 7 fflush(stdout);
When you continue stepping through the program, control does not
pass back to factorial()
to compute the factorial
of 3
(see Example 7-18).
Instead, the next line that is executed is the last line of
main()
. The cont
command instructs
the debugger to resume running the program. The program finishes
executing without printing the factorial of 3.
(ladebug) step stopped at [main:9 0x120000b84] 9 } (ladebug) cont Thread has finished executing (ladebug)
The problem in Example 7-9 may lie in the
bounds of the for
construct:
for (i=1 ; i<3 ; i++) {
Careful examination confirms this hypothesis. When the variable
i
is incremented to 3,
the statement
i
< 3
is false and a loop exits.
To fix the problem, change the statement as follows:
for (i=1 ; i<=3 ; i++) {
To make the change, edit the for
construct in the
source file and recompile the program.
To edit the source file, choose one of the following:
run
or
rerun
command, the debugger will use the new
program. Previously set breakpoints and traces will still be
active.
quit
command to leave the debugger
then edit the source file, recompile the program, and test
the program. If you want to retest the program under debugger
control, invoke the debugger again. After you quit the debugger,
all breakpoints, tracepoints, and other program-specific settings
are lost.
Another way to troubleshoot the problem in Example 7-9 is to trace the value of variable i
as the program executes. If you have not yet exited the debugger,
the breakpoint at line 4 is still active. You can enter the
delete
command to delete the breakpoint at line 4. In
Example 7-19, the status
command is used to list the breakpoints or tracepoints that are
currently active and the delete
command is used to
remove the breakpoint at line 4.
(ladebug) status #1 PC==0x120001180 in main "sample.c":4 { break } (ladebug) delete 1 (ladebug) status (ladebug)
You can use the trace
command to monitor program
variables and to monitor when functions are entered and exited.
With this command, you can set a tracepoint on a variable that is
visible from the current context at the time you set the breakpoint.
The syntax is as follows:
trace variable
When you run your program under debugger control, the debugger will
print a message when the variable changes value. The trace
command works at the function level; when a traced variable
is evaluated, and the subsequent message is printed when program
execution begins each function, not at the line of code
that caused the variable value to change.
In Example 7-20:
main
main
using the run
command
trace
command and resume program execution using the cont
command
(ladebug) stop in main [#2: stop in main ] (ladebug) run [2] stopped at [main:4 0x120000a40] 4 for (i=1 ; i<3 ; i++) { (ladebug) trace i [#3: trace i ] (ladebug) cont [3] Value of i changed before "sample.c":13 Old value = 0 New value = 1 1! = 1 [3] Value of i changed before "sample.c":13 Old value = 1 New value = 2 2! = 2 [3] Value of i changed before "../exit.c":12 Old value = 2 New value = 3 Thread has finished executing (ladebug) "
Tracing variable i
reveals that the loop did not
execute when i
was equal to 3.
This was
the same conclusion reached earlier by stepping through program
execution.
In this example, the trace
command works well, even
though traces are computationally intensive and can dramatically
slow down program execution. For this reason, using traces may
not be appropriate in every situation. Each debugging problem is
different, and you may need to try several different methods on a
program before you uncover the problem.
Ladebug allows you to attach to a running process that is not under debugger control and debug the process.
Use the attach
command to connect to a running
process by specifying the process ID and the associated image
file. Ladebug allows you to switch to debugging another process
in the same session by attaching to it or by loading it (see Section 20.4 for information about loading
a program). After you attach to a running process, you debug the
process as you would any process that is loaded by the debugger.
For common user scenarios for debugging attached processes, see Section 9.13.
You cannot issue the run
or rerun
command on an attached process.
There are two ways to attach to a process. From the command line, the syntax is as follows:
$ ladebug -pid process_id
From the Ladebug prompt:
(ladebug) attach process_id image_file
The sample program in Example 7-9 has been modified slightly to show how to attach to (and detach from) a running process.
#include <stdio.h> #include <unistd.h> main() { int i,f; sleep (20); for (i=1; i<3; i++) { f = factorial(i); printf("%d! = %d\n",i,f); fflush(stdout); } } factorial(i) int i; { if (i<=1) return (1); else return (i * factorial(i-1) ); }
Ladebug does not stop the running process when attaching to it. You
must type Ctrl/C to stop the process. You can also set the debugger
variable $stoponattach
to 1 to cause Ladebug to stop
the attached process after attaching to it.
Example 7-21 shows how to attach to a process in a debugging session.
$ factorial & [1] 32625 1 $ ladebug -pid 32625 factorial 2 Welcome to the Ladebug Debugger Version 4.0-10 ------------------ object file name: factorial Reading symbolic information ...done 3 Attached to process id 32625 .... 4 ^CThread received signal INT 5 stopped at [<opaque> __usleep_thread(): ??? 0x3ff800ec850] (ladebug) stop in factorial 6 [#1: stop in int factorial(int) ] (ladebug) detach 7
The detach
command lets you detach the debugger
from the previously attached process, based on the process ID you
specify from the process ID list. (See Example 7-21 for an example of attaching and detaching a
process.)
The syntax for the detach
command is as follows:
detach [process_id]
The process_id
parameter indicates the process
to which the debugger is attached. If you do not specify the
process_id
parameter, Ladebug detaches from the
current process.
Detaching from a process removes the process information from the debugger, and disables your ability to debug the process.
Ladebug removes all user-specified breakpoints from the detached process. The process continues its program execution.
If other processes are attached to the process being debugged, the
detach
command will not change the process's state.
The kill
command terminates the process that executes
the program. If the process you terminate has child processes
associated with it, they are terminated also.
When a process is terminated, its process objects are not deleted
and Ladebug retains the symbolic debugging information. You can
issue run
and rerun
commands on that
application again.
Use the kill
command to terminate a one or more
processes. The syntax of the kill
command does not
take an argument.
The quit
command will kill a process that was started
by Ladebug.
While displaying a process, you can stop it at any time by typing Ctrl/C.