Appendixes
What Is the Ladebug Debugger?
This manual explains how to use the Compaq Ladebug debugger for Version
67 or higher. Earlier versions of the debugger may not support all the
capabilities documented in this manual.
Ladebug is a symbolic source-level debugger with a choice
of command line or
graphical user interface.
Ladebug provides extensive support for debugging programs written in
C, C++, and Fortran (77 and 90) for the following operating systems:
- Compaq Tru64TM UNIX systems
- Linux for Compaq AlphaTM systems
Ladebug also offers limited support for COBOL and Ada programming.
The official Software Product Description is
part of the Developers' Toolkit Software Product Description (SPD 44.36).
Obtaining a Ladebug Kit
Ladebug kits, manuals, and answers to the frequently asked questions (FAQs) are
available from the following sources:
This manual is intended for programmers who have a basic understanding of one of the
programming languages that the Ladebug debugger supports (C, C++, Fortran, COBOL, and Ada)
and the Compaq Tru64 UNIX or Linux for Alpha operating systems.
This manual is organized as follows:
- Part I contains a quick introduction to the debugger.
- Chapter 1 contains all the information you need to make simple use
of the debugger.
- Part II contains most of the information you need to make expert use of the
debugger.
- Chapter 2 describes preparing your program for debugging.
- Chapter 3 describes starting the debugger.
- Chapter 4 describes giving commands to the debugger.
- Chapter 5 describes the context for executing commands.
- Chapter 6 describes running your program under debugger control.
- Chapter 7 describes locating the site of a problem.
- Chapter 8 describes how to examine the code, the
data, and previously obtained information.
- Chapter 9 describes modifying the process.
- Chapter 10 describes continuing execution of the process.
- Chapter 11 describes snapshots as an undo mechanism.
- Chapter 12 describes debugging optimized code.
- Chapter 13 describes support limitations.
- Part III contains advanced reference information.
- Chapter 14 describes preparing a program for debugging.
- Chapter 15 describes the debugger's syntax and grammar.
- Chapter 16 describes debugging core files.
- Chapter 17 describes kernel debugging
and remote kernel debugging using the
kdebug
debugger.
- Chapter 18 describes machine-level debugging.
- Chapter 19 describes parallel debugging.
- The appendixes contain the following information:
Related Documentation
The following documents contain related information:
- Alpha Architecture Handbook
- Alpha Architecture Reference Manual
- Compaq Fortran Language Reference Manual
- Developing Ada Programs on Tru64 UNIX Systems
- The Annotated C++ Reference Manual, by Margaret E.
Ellis and Bjarne Stroustrup, 1990, Addison-Wesley Publishing Company
- Reference pages:
ladebug
(1)
cxx
(1) for C++
cc
(1) for C
ada
(1) for Ada
cobol
(1) for COBOL
f77
(1) for Fortran 77
f90
(1) for Fortran 90
Reporting Problems
Send mail to
Ladebug.Support@Compaq.com.
What to Report
Please provide the following information when you enter your problem report.
Doing so will make it easier for us to reproduce and analyze your problem.
If you do not provide this information, we may have to ask you for it.
-
A description of the problem. The clearer and more detailed the description,
the easier it will be for us to reproduce and analyze your problem.
-
A transcript of the debugger output. You can obtain this by using the
record io
Ladebug command or by using the
script
(1) system command.
-
Operating system and version information. The output of
uname -a
is best.
-
Ladebug version information. The version number is in the welcome banner that
displays when you invoke the debugger. You can also obtain the version number by
invoking the debugger with the
ladebug -V
command.
-
The smallest source code example possible; build instructions (a Makefile
is preferable); source languages, compiler versions, and so forth; and a pointer to
a
tar
file containing sources or binaries that reproduce the problem.
To obtain compiler versions, you can use the -V
option if your compiler
supports it (see the reference page for your compiler). Alternatively,
you can generate the output of /usr/sbin/setld -i
showing the installed compiler
subsets.
-
The exact Ladebug commands that cause the problem to occur.
-
Any other information that you think would be helpful.
The Ladebug development team can use ftp
to fetch sources and executables
if you can place them in an anonymous FTP area. If not, you may be asked
to use another method.
The following conventions are used in this manual:
Convention |
Meaning |
%
|
A percent sign (%) represents the C shell system prompt.
|
#
|
A pound sign (#) represents the default superuser prompt.
|
UPPERCASE
lowercase
|
The Tru64 UNIX operating system differentiates between lowercase and
uppercase characters. On the operating system level, you must type
examples, syntax
descriptions, function definitions, and literal strings that appear in
text exactly as shown.
|
Ctrl/C
|
This symbol indicates that you must press the Ctrl key while you
simultaneously press another key (in this case, C).
|
monospaced text
|
This typeface indicates a routine, partition,
pathname, directory, file, or non-terminal name. This typeface is also used
in interactive examples.
|
monospaced bold text
|
In interactive examples, this typeface indicates input that you enter.
In syntax statements and text, this typeface indicates the exact name of
a command or keyword.
|
monospaced italic text
|
Monospaced italic type indicates variable values, place holders,
and function argument names.
In syntax definitions, monospaced italic
text indicates non-terminal names. When a non-terminal name consists of
more than one word,
the words are joined using the underscore (_), for example,
breakpoint_command .
|
italic text
|
Italic type indicates book names or emphasized terms.
|
foo_bar
: item1
| item2
| item3
|
A colon starts the syntax definition of a non-terminal name (in this example,
foo_bar. Vertical
bars separating items that appear in syntax definitions indicate
that you choose one item from among those listed.
|
[]
|
In syntax definitions, brackets indicate items that are optional.
|
option ;...
option ,...
option ... |
A set of three horizontal ellipses indicates that you can enter additional
parameters, options, or values. A
semicolon, comma, or space preceding the
ellipses indicates successive items must be separated by
semicolons, commas, or spaces.
|
setld (8)
|
Cross-references to online reference pages include the appropriate
section number in parentheses. For example,
setld (8)
indicates that you can find the material on the
setld
command in Section 8 of the reference pages. The man
command % man 8 setld shows the reference page for
this command.
|
In this manual, some examples show Ladebug version numbers as "Version n".
When you use the debugger, the actual version numbers appear on your screen.
Part I
A Quick Introduction to Using the Ladebug Debugger
Part I provides all the information you need to make simple
use of the debugger.
Chapter 1Overview
You look for a bug by doing the following:
-
Find a repeatable reproducer of the bug the simpler the reproducer is,
the simpler the following steps will be to do.
-
Prepare your program for debugging.
-
Start the debugger.
-
Give commands to the debugger.
-
Command the debugger to do one of the following:
-
Prepare to create a process
running the program.
-
Attach to and interrupt a process
that you created using normal UNIX methods.
-
Command the debugger to create breakpoints
that will pause the process as close as possible to where the bug
happened.
-
If you are using the debugger to create the process, tell it to
create the process now.
-
Do whatever it takes to reproduce the bug, so that the
breakpoints will stop the process close to where
the bug has caused something detectably wrong to happen.
-
Look around to determine the location of the bug:
- If the bug is in code where the debugger has stopped the process, exit
the debugger and fix the bug.
-
If the bug has not happened yet, remove any
breakpoints that are triggering too often,
create other breakpoints that work better at locating the problem, and
continue the process.
-
If the bug has already occurred, take the same steps of
creating breakpoints and so on, except with the process running backward.
Unfortunately, reverse execution is a difficult problem (how do you un-erase that
disk?), so the compilers and the debugger do not support it. Instead,
you have to rerun from an earlier position (a
snapshot if you made one, or else the beginning of
the program), first creating breakpoints that stop
the process sooner.
1.1 Preparing a Program for Debugging
Compile and link your program using the -g
switch, as follows:
If the problem only occurs in optimized code, use the -g3
switch.
1.2 Starting the Debugger
Before you start the debugger, make sure that you
have correctly set the size information
for your terminal; otherwise, the debugger's command line editing support
may act unpredictably. For example, if your terminal is 47x80, you may need
to set the following:
% stty rows 47 ; setenv LINES 47
% stty cols 80 ; setenv COLS 80
Following are four basic alternatives for running the debugger on a process:
When you enter commands, you use the left and right arrow keys to move
within the line and the up and down arrow keys to recall previous
commands for editing. When you finish entering a command, press the Enter key
to submit the completed line to the debugger for processing.
You can continue a line by ending the line to be continued with a
backslash (\) character.
On a blank line, press the Enter key to re-execute the most-recent valid command.
Following are two very useful commands:
(ladebug) help
(ladebug) quit
1.4 Scripting or Repeating Previous Commands
To execute debugger commands from a script, use the source
command as follows:
(ladebug) source filename
The source
command causes the debugger to read and execute
Ladebug commands from
filename
.
Although the debugger supports debugging multiple processes, it operates only on a
single process at a time, known as the current process.
Processes contain one or more threads of execution. The threads execute
functions. Functions are sequences of instructions that come from source
lines within source files.
As you enter debugger commands to manipulate your process, it would be very
tedious to have to repeatedly specify which thread, source file, and so on you wish
the command to be applied to. To prevent this, each time the debugger stops
the process, it re-establishes a static context and a dynamic context for your
commands. The components of the static context are independent of this run of
your program; the components of the dynamic context are dependent on this run.
- The static context consists of the following:
- A current program
- A current file
- A current line
- The dynamic context consists of the following:
- A current call frame
- A current thread
- The particular thread executing the event that caused the debugger to gain
control of the process
You can change most of these individually to point to other instances, as
described in the relevant portions of this manual, and the debugger will modify
the rest of the static and dynamic context to keep the various components consistent.
1.6 Running a Program Under Debugger Control
As was shown previously, you can tell the debugger
to create a process or to attach to an existing process.
After you specify the program (either on the shell command line or
by using the load
command), but before you have requested the debugger to
create the process, you can still do things that seem to require a running
process; for example, you can create breakpoints and examine sources. Any breakpoints
that you create will be inserted into the process as soon as possible after it
executes your program.
To have the debugger create a process (rather than attach to an existing
process), you request it to run,
specifying, if necessary, any
input and output redirection and
arguments as follows:
% ladebug a.out
Welcome to the Ladebug Debugger Version n
(ladebug) run
or
(ladebug) run args
or
(ladebug) run > output-file
or
(ladebug) run args > output-file < input-file
The result of using any of the preceding command variations is similar to
having attached to a running process. The rerun
command repeats the previous run command with the
same arguments and file redirection.
1.7 Pausing the Process at the Problem
Following are the four most common ways to pause a process:
Wait until the process raises some signal. It will do this when there is
an arithmetic exception, an illegal instruction, or an unsatisfiable memory
access, such as an attempt to write to memory for which protection is set to
read-only. For example:
(ladebug) run
Thread received signal SEGV
stopped at [void buggy(char*, char*):13 0x120001ba4]
13 output[k] = input[k];
Create a breakpoint before you run or continue the process:
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run
[1] stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
Create a watchpoint before you run or continue the process:
(ladebug) watch variable nodeList._firstNode write
[#2: watch variable nodeList._firstNode write ]
(ladebug) cont
[2] Address 0x11fffbf91 was accessed at:
List<Node>::List(void): x_list.cxx
[line 121, 0x120001d94] stq r31, 0(r1)
0x11fffbf90: Old value = 0x000003ffc0002200
0x11fffbf90: New value = 0x0000000000000000
[2] stopped at [List<Node>::List(void):123 0x120001d98]
123 }
1.8 Examining the Paused Process
This section describes how to examine components of the paused
process.
1.8.1 Looking at the Source Files
You can perform the following operations on source files:
- Tell the debugger where
your sources are, if it cannot find them.
- Find out the name of the current source file.
- Switch to a different source file.
- List lines in a source file.
- Search within a source file.
Following is an example that shows listing lines and using the
/
command to search for a string:
% ladebug a.out
(ladebug) file
x_list.cxx
(ladebug) list 180: 10
180 main()
181 {
182 List<Node> nodeList;
183
184 // add entries to list
185 //
> 186 IntNode* newNode = new IntNode(1);
187 nodeList.append(newNode);
188
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
(ladebug) /CompoundNode
192 CompoundNode* cNode1 = new CompoundNode(3.1415, 7);
Aliases are shorthand forms of longer commands.
This example shows using the W
alias, which lists up to 20 lines
around the current line. A right bracket (>) marks the current line.
(ladebug) alias W
W list $curline - 10:20
(ladebug) W
176
177
178 // The driver for this test
179 //
180 main()
181 {
182 List<Node> nodeList;
183
184 // add entries to list
185 //
> 186 IntNode* newNode = new IntNode(1);
187 nodeList.append(newNode);
188
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
190 nodeList.append(cNode);
191
192 nodeList.append(new IntNode(3));
193
194 IntNode* newNode2 = new IntNode(4);
195 nodeList.append(newNode2);
1.8.2 Looking at the Threads
(Tru64 UNIX Only)
In a multithreaded application, you can
obtain information about the thread that
stopped or about all the threads, and you can
then change the context to look more closely
at a different thread. A right bracket (>) marks the current thread.
The asterisk (*) indicates the last interrupted thread.
(ladebug) thread
Thread Name State Substate Policy Pri
------ ------------------------- --------------- ----------- ------------ ---
>* 1 default thread running VP 3 SCHED_OTHER 19
(ladebug) show thread
Thread Name State Substate Policy Pri
------ ------------------------- --------------- ----------- ------------ ---
>* 1 default thread running VP 3 SCHED_OTHER 19
-1 manager thread blk SCS SCHED_RR 19
-2 null thread for slot 0 running VP 1 null thread -1
-3 null thread for slot 1 ready VP 3 null thread -1
-4 null thread for slot 2 new new null thread -1
-5 null thread for slot 3 new new null thread -1
2 threads(0x140000798) blocked cond 3 SCHED_OTHER 19
3 threads+8(0x1400007a0) blocked cond 3 SCHED_OTHER 19
4 threads+16(0x1400007a8) blocked cond 3 SCHED_OTHER 19
5 threads+24(0x1400007b0) blocked cond 3 SCHED_OTHER 19
6 threads+32(0x1400007b8) blocked cond 3 SCHED_OTHER 19
You can select any thread to be the focus of commands that show things.
For example:
(ladebug) thread 2
Thread Name State Substate Policy Pri
------ ------------------------- --------------- ----------- ------------ ---
> 2 threads(0x140000798) blocked cond 3 SCHED_OTHER 19
1.8.3 Looking at the Call Stack
You can examine the call stack of any thread.
Even if you are not using threads explicitly, your process will have one thread
running your code. You can move up and
down the stack, and
examine the source being
executed at each call. For example:
(ladebug) where 4
>0 0x12000226c in ((Node*)0x140004000)->Node::Node() "x_list.cxx":77
#1 0x1200022b8 in ((IntNode*)0x140004000)->IntNode::IntNode(data=2) "x_list.cxx":88
#2 0x120002358 in ((CompoundNode*)0x140004000)->CompoundNode::CompoundNode(fdata=12.345000267028809, idata=2) "x_list.cxx":101
#3 0x12000251c in main() "x_list.cxx":189
(ladebug) up 2
>2 0x120002358 in ((CompoundNode*)0x140004000)->CompoundNode::CompoundNode(fdata=12.345000267028809, idata=2) "x_list.cxx":101
101 CompoundNode::CompoundNode(float fdata, int idata)
(ladebug) list $curline - 10: 20
91 void IntNode::printNodeData() const
92 {
93 cout << " type is integer, value is ";
94 cout << _data << endl;
95 }
96
97
98 //=============================================================================
99 // CompoundNode definition
100 //
> 101 CompoundNode::CompoundNode(float fdata, int idata)
102 :
103 IntNode(idata),
104 _fdata (fdata)
105 {
106 }
107 void CompoundNode::printNodeData() const
108 {
109 cout << " type is compound, value is ";
110 cout << _fdata << endl;
(ladebug) down 1
>1 0x1200022b8 in ((IntNode*)0x140004000)->IntNode::IntNode(data=2) "x_list.cxx":88
88 IntNode::IntNode(int data) : _data(data)
1.8.4 Looking at the Data
You can look at variables and
evaluate expressions involving them. For example:
(ladebug) print fdata
12.345000267028809
(ladebug) print idata
2
(ladebug) print idata + 59
61
(ladebug) print this
0x140004000
(ladebug) print *this
class CompoundNode {
_fdata = 0;
_data = 0; // class IntNode
_nextNode = 0x0; // class IntNode::Node
}
1.8.5 Looking at the Signal State
The debugger shows you the signal that stopped the thread. For example:
(ladebug) run
Thread received signal SEGV
stopped at [void buggy(char*, char*):13 0x120001ba4]
13 output[k] = input[k];
1.8.6 Looking at the Generated Code
You can print memory as instructions or
as data. In the following example, the
wi
alias lists machine instructions
before and after the current instruction. An asterisk (*)
marks the current instruction.
(ladebug) alias wi
wi ($curpc - 20)/10 i
(ladebug) wi
CompoundNode::CompoundNode(float, int): x_list.cxx
[line 105, 0x120002348] cpys $f17,$f17,$f0
[line 105, 0x12000234c] bis r31, r18, r8
[line 101, 0x120002350] bis r31, r19, r16
[line 101, 0x120002354] bis r31, r8, r17
[line 101, 0x120002358] bsr r26, IntNode::IntNode(int)
*[line 101, 0x12000235c] ldq r18, -32712(gp)
[line 101, 0x120002360] lda r18, 48(r18)
[line 101, 0x120002364] stq r18, 8(r19)
[line 101, 0x120002368] sts $f0, 24(r19)
[line 106, 0x12000236c] bis r31, r19, r0
(ladebug) $pc/10x
0x12000235c: 0x8038 0xa65d 0x0030 0x2252 0x0008 0xb653 0x0018 0x9813
0x12000236c: 0x0400 0x47f3
(ladebug) $pc/6xx
0x12000235c: 0xa65d8038 0x22520030 0xb6530008 0x98130018
0x12000236c: 0x47f30400 0x47f5041a
(ladebug) $pc/2X
0x12000235c: 0x22520030a65d8038 0x98130018b6530008
You can examine individual registers by
using the print
command. To look at all the
registers, use the printregs
command. For example:
(ladebug) print $r16
5368717312
(ladebug) printx $r16
0x140002000
(ladebug) printregs
$r0 [$v0] = 1 $r1 [$t0] = 5
$r2 [$t1] = 4 $r3 [$t2] = 3
$r4 [$t3] = 2 $r5 [$t4] = 5
$r6 [$t5] = 0 $r7 [$t6] = 4
$r8 [$t7] = 2 $r9 [$s0] = 3
$r10 [$s1] = 0 $r11 [$s2] = 4
$r12 [$s3] = 351841472 $r13 [$s4] = 4
$r14 [$s5] = 340620416 $r15 [$s6] = 1
$r16 [$a0] = 5368717312 $r17 [$a1] = 2
$r18 [$a2] = 2 $r19 [$a3] = 5
$r20 [$a4] = 5368717360 $r21 [$a5] = 4
$r22 [$t8] = 1 $r23 [$t9] = 5
$r24 [$t10] = 4396973371008 $r25 [$t11] = 1
$r26 [$ra] = 4831847228 $r27 [$t12] = 0
$r28 [$at] = 4831845184 $r29 [$gp] = 5
$r30 [$sp] = 4831834640 $r31 [$zero]= 0
$f0 = 12.34500026702881 $f1 = 0
$f2 = 0 $f3 = 0
$f4 = 0 $f5 = 0
$f6 = 0.10000000000000001 $f7 = 0.20000000000000001
$f8 = 0.29999999999999999 $f9 = 0.40000000000000002
$f10 = 0.5 $f11 = 0
$f12 = 0 $f13 = 0
$f14 = 0 $f15 = 0
$f16 = 0 $f17 = 0
$f18 = 0 $f19 = 0
$f20 = 0 $f21 = 0
$f22 = 0 $f23 = 0.59999999999999998
$f24 = 12.34500026702881 $f25 = 0
$f26 = 0.69999999999999996 $f27 = 0.80000000000000004
$f28 = 0.90000000000000002 $f29 = 1
$f30 = 1.1000000000000001 $f31 = 0
$pc = 0x12000226c $ps = 0x8
$fpcr = 0x0 $uniq = 0x0
$vfp = 0x11ffff210
1.9 Continuing Execution of the Process
After you are satisfied that you understand what is going on, you
can move the process forward and see what happens. The following table
shows the aliases and commands you can use to do this:
Desired Behavior |
Alias |
Command |
Can Take Repeat Count |
Continue until another interesting thing happens |
c
|
cont
|
Yes
|
Single step by line, but step over calls |
n
|
next
|
Yes
|
Single step to a new line, stepping into calls |
s
|
step
|
Yes
|
Continue until control returns to the caller |
None |
return
|
No
|
Single step by instruction, over calls |
ni |
nexti
|
Yes
|
Single step by instruction, into calls |
si
|
stepi
|
Yes
|
The following example demonstrates stepping through lines of source code:
(ladebug) list $curline - 10: 20
172
173 if (i == 1) cout << "The list is empty ";
174 cout << endl << endl;
175 }
176
177
178 // The driver for this test
179 //
180 main()
181 {
> 182 List<Node> nodeList;
183
184 // add entries to list
185 //
186 IntNode* newNode = new IntNode(1);
187 nodeList.append(newNode);
188
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
190 nodeList.append(cNode);
191
(ladebug) next
stopped at [int main(void):186 0x120002440]
186 IntNode* newNode = new IntNode(1);
(ladebug) next 9
stopped at [int main(void):200 0x120002714]
200 CompoundNode* cNode2 = new CompoundNode(10.123, 5);
(ladebug) step
stopped at [CompoundNode::CompoundNode(float, int):101 0x120002350]
101 CompoundNode::CompoundNode(float fdata, int idata)
(ladebug) list $curline - 2: 6
99 // CompoundNode definition
100 //
> 101 CompoundNode::CompoundNode(float fdata, int idata)
102 :
103 IntNode(idata),
104 _fdata (fdata)
(ladebug) step
stopped at [IntNode::IntNode(int):88 0x1200022b4]
88 IntNode::IntNode(int data) : _data(data)
(ladebug) list $curline - 2: 5
86 // IntNode definition
87 //
> 88 IntNode::IntNode(int data) : _data(data)
89 {
90 }
(ladebug) return
stopped at [CompoundNode::CompoundNode(float, int):101 0x12000235c]
101 CompoundNode::CompoundNode(float fdata, int idata)
(ladebug) return
stopped at [int main(void):200 0x120002764]
200 CompoundNode* cNode2 = new CompoundNode(10.123, 5);
(ladebug) step
stopped at [int main(void):201 0x120002794]
201 nodeList.append(cNode2);
(ladebug) step
stopped at [void List<Node>::append(class Node* const):148 0x120001dbc]
148 if (!_firstNode)
(ladebug) list $curline: 8
> 148 if (!_firstNode)
149 _firstNode = node;
150 else {
151 Node* currentNode = _firstNode;
152 while (currentNode->getNextNode())
153 currentNode = currentNode->getNextNode();
154 currentNode->setNextNode(node);
155 }
(ladebug) step 2
stopped at [void List<Node>::append(class Node* const):152 0x120001de0]
152 while (currentNode->getNextNode())
The following example demonstrates stepping at the instruction level:
(ladebug) $curpc - 20/14i
void List<Node>::append(class Node* const): x_list.cxx
[line 149, 0x120001dcc] ldq r3, 24(sp)
[line 149, 0x120001dd0] stq r2, 0(r3)
[line 149, 0x120001dd4] br r31, 0x120001e2c
[line 151, 0x120001dd8] ldq r4, 24(sp)
[line 151, 0x120001ddc] ldq r9, 0(r4)
*[line 152, 0x120001de0] bis r31, r9, r16
[line 152, 0x120001de4] ldq r27, -32584(gp)
[line 152, 0x120001de8] jsr r26, (r27), Node::getNextNode
[line 152, 0x120001dec] ldah gp, 8192(r26)
[line 152, 0x120001df0] lda gp, 25940(gp)
[line 152, 0x120001df4] beq r0, 0x120001e14
[line 153, 0x120001df8] bis r31, r9, r16
[line 153, 0x120001dfc] ldq r27, -32584(gp)
[line 153, 0x120001e00] jsr r26, (r27), Node::getNextNode
(ladebug) stepi
stopped at [void List<Node>::append(class Node* const):152 0x120001de4] ldq r27, -32584(gp)
(ladebug) nexti
stopped at [void List<Node>::append(class Node* const):152 0x120001de8] jsr r26, (r27), Node::getNextNode
(ladebug) stepi
stopped at [class Node* Node::getNextNode(void):81 0x120002284] bis r31, r16, r1
(ladebug) return
stopped at [void List<Node>::append(class Node* const):152 0x120001dec]
152 while (currentNode->getNextNode())
(ladebug) nexti 2
stopped at [void List<Node>::append(class Node* const):152 0x120001df4] beq r0, 0x120001e14
1.10 Snapshots as an Undo Mechanism
Often when you move the process forward, you accidentally go too far. For
example, you may step over a call that you should have stepped into.
In a program that does not use multiple threads, you can
use snapshots to save your state before you
step over the call. Then clone that
snapshot to position another process just before the call so you can step into
it.
The following example shows the stages of a snapshot being used in this way:
- The first stage is to build the program and start debugging.
- The next stage is to stop the process just before the call and take a
snapshot. You can
see you are just before the call because the right bracket (>) to the left of
the source list shows the line about to be executed:
(ladebug) next 2
stopped at [int main(void):187 0x1200024b8]
187 nodeList.append(newNode);
(ladebug) list $curline - 10: 20
177
178 // The driver for this test
179 //
180 main()
181 {
182 List<Node> nodeList;
183
184 // add entries to list
185 //
186 IntNode* newNode = new IntNode(1);
> 187 nodeList.append(newNode);
188
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
190 nodeList.append(cNode);
191
192 CompoundNode* cNode1 = new CompoundNode(3.1415, 7);
193 nodeList.append(cNode1);
194
195 nodeList.append(new IntNode(3));
196
(ladebug) save snapshot
# 1 saved at 08:41:46 (PID: 1012).
stopped at [int main(void):187 0x1200024b8]
187 nodeList.append(newNode);
- You now step over the call. The
execution is now after the call, shown by the right bracket (>) being on the
following source line:
(ladebug) next
stopped at [int main(void):189 0x1200024d0]
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
(ladebug) list $curline - 10: 20
179 //
180 main()
181 {
182 List<Node> nodeList;
183
184 // add entries to list
185 //
186 IntNode* newNode = new IntNode(1);
187 nodeList.append(newNode);
188
> 189 CompoundNode* cNode = new CompoundNode(12.345, 2);
190 nodeList.append(cNode);
191
192 CompoundNode* cNode1 = new CompoundNode(3.1415, 7);
193 nodeList.append(cNode1);
194
195 nodeList.append(new IntNode(3));
196
197 IntNode* newNode2 = new IntNode(4);
198 nodeList.append(newNode2);
- Oh, how you wish you hadn't done that! No problem, just clone
that snapshot you made:
(ladebug) clone snapshot
Process has exited
Process 1009 cloned from Snapshot 1.
# 1 saved at 08:41:46 (PID: 1012).
stopped at [int main(void):187 0x1200024b8]
187 nodeList.append(newNode);
- Now you are in a new process before the call is executed:
(ladebug) list $curline - 10: 20
177
178 // The driver for this test
179 //
180 main()
181 {
182 List<Node> nodeList;
183
184 // add entries to list
185 //
186 IntNode* newNode = new IntNode(1);
> 187 nodeList.append(newNode);
188
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
190 nodeList.append(cNode);
191
192 CompoundNode* cNode1 = new CompoundNode(3.1415, 7);
193 nodeList.append(cNode1);
194
195 nodeList.append(new IntNode(3));
196
NOTE: The debugger used fork()
to both
create the snapshot and to clone it.
Part II
A Guide to Using the Ladebug Debugger
Part II provides most of the information needed to make
expert use of the debugger.
Some additional details have been moved to
Part III: Advanced Topics so they do not hinder the reading
of this information.
Chapter 2Preparing a Program for Debugging
To facilitate debugging, you can prepare your source code and the
compiler and linker environment.
2.1 Preparing Your Source Code
You do not need to make changes to the source code to debug the program. However,
you can do the following to make debugging easier:
-
If the source code has functions that can be called to output data structures,
you can call them from the debugger; you may want to create such functions.
- It is a good idea to make the following items part of your source code:
- An initial stall point if you cannot
create the process easily from within the debugger
- Assertions sprinkled liberally through the sources to help locate
errors early
2.2 Preparing the Compiler and
Linker Environment
Debugging information is put into .o
files by compilers. The level of
information is controlled by compiler switches. See the reference page for your
compiler. The switch is probably -g
.
The debugging information is propagated into the a.out
(executable) or
.so
(shared library) by the ld
command.
It is removed by the strip
command. If you strip your programs,
keep the
unstripped version to use with the debugger.
The debugging information can cause .o
files to be very large,
causing
long link times, but even so it can also be incomplete.
If you are debugging C++ applications and you have unused variables in your code,
or if opaque classes, structs, or unions keep showing up in Ladebug, you may
want to compile particular files with the
cxx -gall
and -gall_pattern
switches. See cxx
(1).
If you are debugging optimized code, see the appropriate compiler
documentation for information about various -g
switches and
their relationship to optimization.
Chapter 3Starting the Debugger
You can start the debugger in the following ways:
- From a shell
- From within Emacs
- By calling the
debugBreak
function in your program to start the debugger
at some specified point in the program.
For more information about debugBreak
, see the
Ladebug Web site
FAQ.
This chapter also discusses the
following topics:
When you invoke the debugger from a shell, you can bring a
program, core file, or local kernel under debugger control, or you can
attach to a running process.
The following is the shell syntax to invoke the debugger using the
ladebug
command:
ladebug
[ -c file ]
[ -gui ]
[ -i file ]
[ -I directory ]
[ -interactive ]
[ -k ]
[ -line serial_line ]
[ -nosharedobjs ]
[ -parallel ]
[ -pid process_id ]
[ -prompt string ]
[ -remote ]
[ -rp remote_debug_protocol ]
[ -tty terminal_device ]
[ -V ]
[ executable_file [ core_file ] ]
The following table describes the ladebug
command
options and parameters:
Options and Parameters
| Description
|
-c |
Specifies an initialization command file. The default initialization file is
.dbxinit . During startup, the debugger searches for this file in
the current directory. If it is not there, the debugger searches
your home directory. This file is processed after the target process has been
loaded or attached to. |
-gui |
Activates the debugger's graphical user interface (GUI) on Tru64 UNIX systems only. |
-i |
Specifies a pre-initialization command file. The default pre-initialization
file is .ladebugrc . The debugger
searches for this file during startup, first in the current
directory and then in your home directory. This file is processed before
the debugger has connected to the application being debugged, so that commands
such as set $stoponattach = 1 will have taken effect when the
connection is made. |
-I |
Specifies the directory containing the source code for the target
program, in a manner similar to the use command. Use multiple
-I options to specify more than one directory.
The debugger searches directories in the order in which they were
specified on the command line. |
-interactive |
Causes the debugger to act as though stdin is isatty() ,
regardless of whether or not it is. This flag is sometimes useful when using
rsh to run the debugger. Currently, the only effect is to cause
the debugger to output the prompt to stdout when it is ready for
the next line of input. |
-k or -kernel |
Enables local kernel debugging. |
-line |
Specifies the serial line for remote kernel debugging. You must use this
with -rp .
|
-nosharedobjs |
Prevents the reading of symbol table information for any shared objects
loaded when the process executes. Later in the debug session, you can
enter the readsharedobj command to read the
symbol table information for a specified object. |
-parallel |
In Ladebug Version 67, enables parallel debugging for applications using the
Message Passing Interface
(MPI) or Unified Parallel C (UPC). Earlier versions of Ladebug do not
have this capability. |
-pid |
Specifies the process ID of the process to be
debugged. You cannot use this option with any remote or kernel debugging
flags. |
-prompt |
Specifies a debugger prompt. The default
debugger prompt is (ladebug) . If the prompt
argument contains spaces or special
characters, enclose the argument in quotes (" ").
You can specify a debugger prompt when you start the debugger from a shell
with the -prompt option. The default prompt is
(ladebug) .
% ladebug -prompt ">> " sample
>> quit
You can also change the prompt by setting the
$prompt debugger variable. For example:
(ladebug) set $prompt = "newPrompt>> "
newPrompt>>
|
-remote |
Enables remote kernel debugging for use with the kdebug kernel
debugger. |
-rp |
Specifies the remote debug protocol. Currently only kdebug is
supported; -rp
kdebug enables remote kernel debugging. |
-tty |
Specifies the terminal device for remote kernel debugging. You must use
this with -rp . |
-V |
Causes the debugger to print its version number and exit without starting
a debugging session. |
executable_file |
Specifies the program executable file. |
core_file |
Specifies the core file. |
For example, to invoke the debugger on an executable file:
% ladebug executable_file
To invoke the debugger on a core file:
% ladebug executable_file core_file
To invoke the debugger and attach to a running process:
% ladebug -pid process_id executable_file
To invoke the debugger and attach to a running process when you
do not know what file it is executing:
% ladebug -pid process_id
To start the Ladebug GUI:
% ladebug -gui
To invoke the debugger on the local kernel:
% ladebug -k /vmunix
To invoke the debugger on the remote kernel:
% ladebug -remote vmunix
You can control your debugger process entirely
through the Emacs Grand Unified Debugger (GUD) buffer mode, which is
a variant of shell mode. All the Ladebug commands are available, and you
can use the shell mode history commands to repeat them.
Ladebug Version 4.0-48 and higher supports GNU Emacs Version 19 and higher.
Ladebug Version 4.0-58 and higher supports Lucid XEmacs Version 19.14 and higher.
The information in the following sections assumes
you are familiar with Emacs and are using the Emacs notation for naming
keys and key sequences.
For each Emacs session, before you can invoke
the debugger, you must load the Ladebug-specific Emacs LISP code, as follows:
M-x load-file
At the Load file: prompt, type:
/usr/lib/emacs/lisp/ladebug.el
You can also place a load-file call in your
Emacs initialization file (~/.emacs
). For example:
(load-file "/usr/lib/emacs/lisp/ladebug.el")
To start the debugger with Emacs, type:
M-x ladebug
The following invocation line displays:
Run the debugger (like this): ladebug
Edit the invocation line by typing the target program and pressing Return.
Emacs remembers the invocation. To debug the same program again, you need
only press Return.
Emacs displays the GUD buffer and runs the debugger within it; the debugger
starts and displays its (ladebug)
prompt, indicating readiness.
The GUD buffer saves all of the commands you type and the program output for
you to edit. In general, interact with the debugger in the GUD buffer as you
would with a debugger started from a shell.
One of the benefits of running the debugger from within Emacs is a closer
correlation between program execution and source. When your program stops,
for example at a breakpoint, Emacs displays the source of your program in a
second buffer (source buffer) and indicates the current execution line with =>.
NOTE: If the source is already loaded into a buffer, Emacs often finds
that buffer. However, in some NFS mounting situations, Emacs may use an
alternate name for some directories and will create a second buffer for your
source (often with <2> appended to the name). Be careful that you do not modify
the original buffer or kill it outright.
By default, Emacs sets its current working directory to be the directory
containing the target program. Because the debugger does not do this when invoked
directly, you may need to change the source code search path when
using the debugger from within Emacs. To set an alternate source code search
path, use the
Ladebug map source directory
command.
All Emacs editing functions and GUD key bindings are available.
For example:
-
You can execute a
step
command by typing the command in the GUD
buffer.
-
You can select a line of code in the current source buffer and type a command
to set a breakpoint at that position:
C-x SPC
For more information on Emacs functionality and
key bindings, see the Emacs documentation. For example:
M-x info
Then select the Emacs menu, then the Debuggers menu.
XEmacs will come up with the source buffer displayed. Use C-x 2
and a buffer menu to select the control buffer.
To exit the debugger, use the quit
command:
quit_command
: quit
Alternatively, you can type exit
, which is a predefined
alias for quit
.
To access the online help about debugger commands, use the
help
command:
help_command
: help [ topic ]
Enter help
to see a list of help topics. Enter help
command
to see a list of Ladebug commands. Enter
help ladebug
to see a list of function-oriented Ladebug
commands.
You can start the GUI in either of two ways:
- From the shell command line, by specifying the
-gui
switch.
For example:
% ladebug -gui
- By typing the guion_command.
guion_command
: gui
For example:
(ladebug) gui
When you use the GUI, a Debugger Output window appears, in which you manually
enter commands. You can also use the available controls (push buttons,
menus, and so on) to execute debugger commands.
You can shut down the GUI and leave the command line session running by
choosing File/Close All. In this case, you can restart the GUI any time
with the Ladebug gui
command.
To end the command line session and exit the GUI, choose File/Exit Debugger in
the GUI window.
Chapter 4Giving Commands to the Debugger
The debugger has several different mechanisms you can use to direct its
behavior. It receives input from the following:
- Environment variables
- Shell command line
stdin
, which is usually one of the following:
- A terminal
- A file
- A pipe connecting the debugger to an editor (usually
Emacs)
- Graphical user interface (GUI)
- Other files:
- At startup, before attaching to or starting the target executable
and before processing command line qualifiers, commands in:
-
./.ladebugrc
, if available, otherwise
-
~/.ladebugrc
, if available
- Just before accepting command input from you:
-
./.dbxinit
, if available, otherwise
-
~/.dbxinit
, if available
- Files that specify the X11 and Motif default appearance;
the debugger searches in the following order:
~/ladebugresource
./ladebugresource
/usr/lib/X11/app-defaults/ladebugresource
- Files specified in the
source command
Some examples of the difference between .ladebugrc
and
.dbxinit
are shown in the following table:
Example Command |
If Used in .ladebugrc |
If Used in .dbxinit |
Assume the command "set
$stoponattach = 1" is in one of these
files and you invoked the debugger as:
% ladebug -pid process_id executable_file
|
The debugger attaches and stops.
|
The debugger attaches and waits for you to
press Ctrl/C; subsequent attaches will stop.
|
Assume the command "stop in main " is in one of these
files:
|
The debugger generates a message that there is no
main in which to place a breakpoint, because there is no target yet.
|
The debugger sets the breakpoint
(assuming there is a main in the target).
|
This chapter discusses the following topics:
4.1 Debugger's Command Processing Structure
The debugger processes commands as follows:
- Prompts for input.
- Obtains a complete line from the input file
and performs:
- Parses the entire line according to the parsing rules for the current
language.
- Executes the commands.
4.2 Interrupting a Debugger Action
To interrupt program execution or to abort a debugger action, press Ctrl/C.
This returns the debugger to the prompt.
4.3 Entering and Editing Command Lines
The debugger reads lines from stdin
. The debugger supports command
line editing when processing stdin
if stdin
is a
terminal and the
debugger variable $editline
is non-zero (the default; see the
set
command to change it). For this to work
correctly, you must set the terminal width to
the correct value. After editing, press the Return key to send the line to the
debugger.
-
Use the left and right arrow keys to edit parts of the line.
-
Use the up and down arrow keys to recall and edit earlier commands.
NOTE: When you use the up and down arrow keys, the debugger skips duplicate
commands. To see a complete list of the commands you have entered, use the
history
command.
The debugger copies each line from stdin
to the
record input file,
if you have requested that file.
The debugger scans each line from the beginning, looking for backslash (\)
characters, which 'quote' the immediately following character. If the line ends
in a quoted newline, then another line is similarly processed from stdin
and
appended to the first one, with the quoted newline removed.
Whether or not command line editing is enabled, you can always use your
terminal's cut-and-paste function to avoid excessive typing while entering input.
4.3.1 History Replacement of the Line
Leading spaces and tabs are removed from the assembled line.
For assembled lines that begin with an exclamation
point (!), the following
rules apply:
- If the second character is also an exclamation point (!), the assembled
line is replaced by the most-recent entry from the history list. Any remaining characters
after !! are appended to the assembled line.
- Otherwise, spaces and tabs are skipped, and one of the following actions
occurs:
For lines that begin with a caret (^), these rules apply:
- The line is analyzed to extract the following:
- The characters following the first caret but before a
second caret, or until the end of line. These characters
are the target string.
- If there is a second caret, the characters following it but
before a third caret, or until the end of line. These characters
are the replacement string.
- If there is a third caret, the characters following it to the end
of the line. These characters are the append string.
- The most-recent entry from the history list is checked to see if it has
an occurrence of the target string. If it does not, an error is reported.
- The assembled line is replaced by this most-recent entry, except that
the first occurrence of the target string is replaced by the
replacement string (possibly zero length), and the append string is appended
to the assembled line.
The assembled line is now appended to the history list.
You cannot use exclamation points and carets in command lists built with
braces ({});
for example, {print3; !!3}
will not parse.
You can use them in scripts.
History in a command list is not limited by braces, but goes all the way back.
For example:
(ladebug) print 1
1
(ladebug) stop in main { print 2; history 3}
[#1: stop in int main(void) { print 2; history 3} ]
(ladebug) run
2
11: print 1
12: stop in main {print 2; history 3}
13: run
[1] stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
NOTE: Commands in breakpoint action lists are
not entered into the history list, thus
print2
and history 3
are not in the history list. The history
command
itself does go in the history list.
The assembled line is now subjected to alias expansion.
This is done by scanning the
line, looking for pound (#), semicolon (;), and left brace ({) characters
that are not inside strings.
- Strings are recognized by their opening and
closing double or single quotes. Backslash quotation causes a quote
character not to terminate the string.
- Pound (#) characters and all that follow to the end of the line
are discarded, unless the pound character is the very first character in the
line. If that is the case,
the pound character is not discarded because
a completely empty line has special meaning.
An exception is made for pound (#) characters that are
surrounded by non-whitespace characters, such as
"file#name". This is needed because the
tmpnam
standard library function generates file and directory names containing
pound (#) characters.
The debugger performs alias expansion as follows:
- At the beginning of the line, and immediately after semicolon (;) or
left brace ({)
characters not inside strings, the debugger checks for the occurrence of
an alias identifier.
- If it finds an alias identifier, it associates the formal parameters of
the alias with the specified actual parameters.
If the alias has no formal parameters, this match consumes no more of the input.
- If there are formal parameters, white space is skipped, and then a '('
character is checked for and skipped. The characters following the '('
up to the first non-nested ',' or ')' character are associated with the
formal parameter.
Again, the characters within strings are not tested.
Nesting is caused by '(' and ')' characters outside of strings.
- If there are more formal parameters, the ',' character is treated as the
terminator of the actual parameter. It is skipped and processing continues
as for the first parameter.
-
After the alias and the correct number of actuals have been identified, all the characters
from the start of the alias identifier to its end (no parameters) or the trailing ')' (one
or more parameters) are replaced by the expansion.
-
Within the definition of the alias, all occurrences of the formal parameter are
replaced
by the actual parameter, regardless of whether or not it is in a string.
The debugger expands environment variables and the leading tilde (~) in the
following cases:
- As part of a command in which a file name or a directory is expected
- In the arguments to
run/rerun
As in any shell, you can group an environment variable name using a pair of
curly braces ({...}), and quote a dollar sign ($) by preceding it with
a backslash (\).
The following table shows how various environment variables expand. It
assumes that the home directory is
/usr/users/hercules
and the environment variable BIN
is
/usr/users/hercules/bin
.
Command with Environment Variable |
Expands into: |
load ~/a.out |
load /usr/users/hercules/a.out |
load $BIN/a.out |
load /usr/users/hercules/bin/a.out |
load ${BIN}2/a\$b |
load /usr/users/hercules/bin2/a$b |
map source directory $BIN ${BIN}2 |
map source directory \
/usr/users/hercules/bin/usr/users/\
hercules/bin \
/usr/users/hercules/bin/usr/users/\
hercules/bin2
|
stop at "$BIN/a.out":20 |
stop at "/usr/users/hercules/\
bin/a.out":20 |
run $BIN/a.out ~/core |
run /usr/users/hercules/bin/a.out \
/usr/users/hercules/core
|
The debugger has different parsing rules for each of the different languages
it supports. A line is processed according to the current language,
even if executing the line will change the current language.
For the debugger to parse the line, it must first turn the line into
a sequence of tokens, a process called "tokenizing" or "lexical
analysis." Tokenizing is done with a state machine.
As the debugger starts tokenizing a line into a command, it starts
processing the characters using the lexical state
LKEYWORD. It uses the rules for lexical tokens in this state,
recognizing the longest sequence of characters
that forms a lexical token.
After the lexical token is recognized, the debugger appends it to the
tokenized form of the line, perhaps changes the state of the tokenizer,
and starts on the next token.
For more detailed information on lexical elements, see
Lexical Elements of Commands in Part III.
Each command line must parse as one of the following:
input
: command_list
| comment
A command_list
is a sequence of commands that are executed one
after the other:
command_list
: command ;...
| command ;
| command
A comment
is a line that begins with
a pound (#) character:
comment
: #
Any text after an unquoted pound character is ignored by the debugger. If the first
non-whitespace character on a line is a pound character, the whole line is
ignored.
NOTE:
The difference between a blank command line and a command line
that is a comment is that a blank line entered from the keyboard
causes the debugger to repeat the previous command and the
comment line does not. Blank lines not entered from the keyboard
are treated as comment lines.
Commands usually start with, and often contain, keywords. These keywords must
be lowercase.
Following is a list of debugger command categories:
command
: alias_command
| attach_command
| braced_command_list
| breakpoint_command
| browse_source_command
| call_stack_command
| command_repetition_command
| continue_command
| detach_command
| dbgvar_command
| edit_file_command
| environment_variable_command
| execute_commands_from_file_command
| execute_shell_command
| guion_command
| help_command
| history_command
| if_command
| kernel_debugging_command
| kill_command
| load_command
| look_around_command
| machinecode_level_command
| modifying_command
| multiprocess_command
| parallel_debugging_command
| quit_command
| record_command
| run_command
| snapshot_command
| shared_library_command
| thread_command
| unload_command
4.4.4 Keywords Within Commands (Ladebug
Version 65 or Higher)
If the identifiers
thread
,
in
,
at
, and if
occur within the
expression
in the
following commands, the debugger treats them as keywords
unless they are enclosed within parentheses (()
):
For example, if your program has thread
defined as an
integer function returning "3", you might enter the following command
to inspect the first thread
levels of the stack:
(ladebug) where thread (3)
Stack trace for thread 3
>0 0x120001244 in c() "x_whereAmbigParse.c":7
#1 0x120001258 in b() "x_whereAmbigParse.c":12
#2 0x120001268 in a() "x_whereAmbigParse.c":13
#3 0x120001278 in main() "x_whereAmbigParse.c":17
#4 0x1200011c8 in __start(...) in /usr/examples/x_whereAmbigParse
But as you can see, this command was parsed in a way that
treated the routine name thread
as a
keyword and displayed a full stack trace for thread 3.
The following version of the command wraps thread
and its arguments in extra parentheses to force the parse
to evaluate thread
as part of an expression rather
than as a keyword:
(ladebug) where (thread(3))
>0 0x120001244 in c() "x_whereAmbigParse.c":7
#1 0x120001258 in b() "x_whereAmbigParse.c":12
#2 0x120001268 in a() "x_whereAmbigParse.c":13
4.4.4.1 Ladebug Version 64 or
Earlier
The debugger treats the identifiers thread
,
in
, at
,
state
, if
,
and with
as keywords unless
they are enclosed within parentheses (()
).
Therefore, if your program contains variables with names the same as
these keywords, to print the variables names enclose them in parentheses.
For example:
(ladebug) print state
line: 24 Unable to parse input as legal command or C++ expression.
(ladebug) print (state)
1
(ladebug) print (3 * state) + 4
7
4.4.5 Using Braces to Make a Composite Command
You can surround a command_list
with braces to make it work like a single command. Some places
require a braced_command_list
just for readability, or to assist
the debugger in understanding your input.
braced_command_list
: { command_list }
4.4.6 Conditionalizing Command Execution
The debugger provides the if
command, whose behavior depends on
the value of an expression.
if_command
: if expression braced_command_list [ else_clause ]
else_clause
: else braced_command_list
In this command, the first braced_command_list
is executed if
expression
evaluates to a non-zero value; otherwise, the
braced_command_list
in the else_clause
is executed, if specified.
For example:
(ladebug) set $c = 1
(ladebug) assign pid = 0
(ladebug) if (pid < $c) { print "Greater" } else { print "Lesser" }
Greater
4.4.7 Debugger Variables
Debugger variables are pseudovariables that exist within the debugger instead of
within your program. They have the following uses:
- Support some limited programming capabilities within the debugger command
language
- Allow you to examine and change various debugger options
- Allow you to find out exactly what various debugger commands did
The following table lists the three different varieties of debugger variables:
User-defined variables |
You create these and
can set them to a value of any type. |
Preference variables |
You modify these to change
debugger behavior. You can only set a preference variable to a value
that is valid for that particular variable. |
Display/state variables |
These variables display the parts of the current debugger state. You
cannot modify them. |
For more information about debugger variables, see
Appendix 1Debugger Variables.
The following commands deal specifically with debugger variables:
dbgvar_command
: set dbgvar_name = expression
| set dbgvar_name
| set
| unset dbgvar_name
The dbgvar_name
should not exist anywhere in your program,
or you may confuse yourself about which of the occurrences you are actually
dealing with. The predefined debugger variables all start with a dollar sign
($), to help avoid this confusion. It is strongly recommended that you
follow the same practice; in a future Ladebug release, all debugger variables
may be required to start with a dollar sign.
NOTE:
If a debugger variable exists that shares a name with a program
variable, and you print an expression involving that name, which of
the two variables the debugger finds is undefined.
The first form creates the
debugger variable if it does not already exist. It then sets the value of the
debugger variable to the result of evaluating the expression. For example:
(ladebug) set $myLoopCounter = 0
(ladebug) print $myLoopCounter
0
The second form is equivalent to the command
set dbgvar_name = 1
. For example:
(ladebug) print $stoponattach
0
(ladebug) set $stoponattach
(ladebug) print $stoponattach
1
The set
form shows all the debugger variables and their
values:
(ladebug) set
$ascii = 0
$beep = 1
$catchexecs = 0
$catchforkinfork = 0
$catchforks = 0
$childprocess = 0
$curevent = 0
$curfile = "x_list.cxx"
$curfilepath = "../src/x_list.cxx"
$curline = 182
$curpc = 0x120002400
$curprocess = 1407196
$cursrcline = 182
$cursrcpc = 0x120002400
$curthread = 3
$dbxoutputformat = 0
$dbxuse = 0
$decints = 0
$doverbosehelp = 1
$editline = 1
$eventecho = 1
$floatshrinking = 1
$funcsig = 1
$giveladebughints = 1
$hasmeta = 0
$hexints = 0
$historylines = 20
$indent = 1
$ladebugpid = 1407191
$lang = "C++"
$lasteventmade = 0
$lc_ctype = "en_US.ISO8859-1"
$listwindow = 20
$main = "\"x_list.cxx\"`main"
$maxstrlen = 128
$memorymatchall = 0
$myLoopCounter = 0
$octints = 0
$overloadmenu = 1
$page = 1
$pagewindow = 0
$parentprocess = 0
$pimode = 1
$prompt = "(ladebug) "
$readtextfile = 0
$regstyle = 1
$repeatmode = 1
$showlineonstartup = 0
$showwelcomemsg = 1
$stackargs = 1
$statusargs = 1
$stepg0 = 0
$stoponattach = 1
$stopparentonfork = 0
$symbolsearchlimit = 100
$threadlevel = "native"
$usedynamictypes = 1
$verbose = 0
To see the value of just one debugger variable,
print
it. For example:
(ladebug) print $catchexecs
0
The unset
form deletes the debugger variable. Some predefined debugger
variables either cannot be deleted or are automatically recreated in the future
when needed. For example:
(ladebug) unset $myLoopCounter
(ladebug) print $myLoopCounter
Symbol "$myLoopCounter" is not defined.
(ladebug) unset $catchforks
Warning: The debugger variable "$catchforks" was not unset because it is a ladebug predefined variable
4.5 Scripting or Repeating Previous Commands
To repeat the last command line, enter two exclamation points
or press the Return key. You can also enter !-1
:
command_repetition_command
: !!
| ! integer
| !- integer
| ! string
To repeat a command line entered during the current debugging
session, enter an exclamation point followed by the integer
associated with the command line. (Use the
history
command
to see a list of commands used.) For example, to repeat the
seventh command used in the current debugging session, enter !7
.
Enter !-3
to repeat the third-to-the-last command. See also
History replacement of the line.
To repeat the most-recent command starting with a string, use the
last form of the command. For example, to repeat a command that
started with bp
, enter !bp
.
Following are other ways to reuse old commands and save typing effort:
-
Use a completely empty line to repeat the last command but not the last line,
which could have been a comment or a syntactically invalid attempt at a
command. Immediately pressing the Return key is the recommended way of doing
this.
-
Use command line editing to recall and modify
commands you have already entered.
-
It is often useful to
have a text editor up and running while
debugging, and use it to assemble
short scripts that you can copy and paste to the debugger. Keep a separate
text file that has such scripts in it, as well as other notes you wish to keep.
This provides continuity from one debugging session to the next, and from one day to
the next.
If you place commands in a file, you can execute them directly from
the file rather than cutting and pasting them to the terminal. For example:
execute_commands_from_file_command
: source filename
| playback input filename
Use the source
command to read and execute commands from a file. (You can also
execute debugger commands
when you invoke the debugger by creating an initialization file named
.dbxinit
.) These commands
can be nested,
and as each comes to an end, reading resumes from where it left off in the
previous file.
Be aware, however, that blank lines in these files do not
repeat the last command, unlike blank lines
entered from the terminal. Format the commands as if they were entered
at the debugger prompt.
Use the pound character (#) to create comments to format your
scripts.
The following is an example debugger script:
(ladebug) sh cat ../src/myscript
step
where 2
The following example shows how to execute it:
(ladebug) run
[1] stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
(ladebug) source ../src/myscript
stopped at [List<Node>::List(void):121 0x120001d74]
121 List<NODETYPE>::List() : _firstNode(NULL)
>0 0x120001d74 in ((List<Node>*)0x11ffff288)->List<Node>::List() "x_list.cxx":121
#1 0x120002400 in main() "x_list.cxx":182
When a command file is executed, the value of the
$pimode
debugger
variable determines whether the commands are echoed. If the $pimode
variable is set to 1, commands are echoed; if $pimode
is set to 0
(the default), commands are not echoed. The debugger output resulting
from the commands is always echoed.
To help you make command files, as well as to help you see
what has happened before, the debugger can write both its input and its
output to files, as follows:
record_command
: record io [ filename ]
| record input [ filename ]
| record output [ filename ]
| unrecord io
| unrecord input
| unrecord output
Use record input
to save Ladebug commands to a file. The
commands in the file can be executed using the source
command or the
playback input
command.
If no file name is specified, the debugger creates a file with a
random file name in /tmp
as the record file. The debugger issues a message
giving the name of that file.
To stop recording debugger input or output, redirect as shown in the
following example, use the appropriate version of the unrecord
command,
or exit the debugger:
(ladebug) record input /dev/null
(ladebug) record output /dev/null
The following example shows how to use the
record input
command to record a series of debugger commands in a
file named myscript
:
(ladebug) record input myscript
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run
[1] stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
(ladebug) record input /dev/null
This example results in the following recorded input in
myscript
:
(ladebug) sh cat myscript
stop in main
run
record input /dev/null
The record output
command saves the debugger output to a file.
The output is simultaneously written to stdout
(normal output) or
stderr
(error messages). For example:
(ladebug) record output myscript
(ladebug) stop in List<Node>::append
[#2: stop in void List<Node>::append(class Node* const) ]
(ladebug) cont
[2] stopped at [void List<Node>::append(class Node* const):148 0x120001d9c]
148 if (!_firstNode)
(ladebug) next
stopped at [void List<Node>::append(class Node* const):149 0x120001da8]
149 _firstNode = node;
After the above commands are executed, myscript
contains the
following:
(ladebug) sh cat myscript
[#2: stop in void List<Node>::append(class Node* const) ]
[2] stopped at [void List<Node>::append(class Node* const):148 0x120001d9c]
148 if (!_firstNode)
stopped at [void List<Node>::append(class Node* const):149 0x120001da8]
149 _firstNode = node;
The record io
command saves both input to and output
from the debugger. For example:
(ladebug) record io myscript
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run
[1] stopped at [int main(void):12 0x120001130]
12 int i;
(ladebug) quit
% cat myscript
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run
[1] stopped at [int main(void):12 0x120001130]
12 int i;
(ladebug) quit
If input or output is already being recorded, a new record input
command will close the old file and record to a new one, rather
than record simultaneously to two files. In that connection,
record io
is equivalent to the combination of
record input
and record output
, and causes any open
recording files to be closed.
The Ladebug prompt itself is only recorded for
record io
.
You can see all the commands you have already entered by using the
history
command. Use history_number
to indicate
how many commands to show, starting with the most recent. If you do not
specify
$historylines
, the 20 previous commands are
shown. See also
History replacement of the line.
history_command
: history [ integer_constant ]
For example:
(ladebug) history 6
18: stop in main
19: run
20: stop in CompoundNode::CompoundNode
21: cont
22: print "history_EXAMPLE START"
23: history 6
4.6 Defining Aliases
You can extend the set of debugger commands by defining aliases.
When the debugger is tokenizing a command
line, it expands aliases and then
retokenizes the expansion:
alias_command
: alias [ alias_name ]
| alias alias_name [ (argument_name ,...) ] string
| unalias alias_name
The following example shows how to define and use an alias:
(ladebug) alias cs
alias cs is not defined
(ladebug) alias cs "stop at 186; run"
(ladebug) cs
[#1: stop at "x_list.cxx":186 ]
[1] stopped at [int main(void):186 0x120002420]
186 IntNode* newNode = new IntNode(1);
The following example further modifies the cs
alias
to specify the breakpoint's line number when you enter the
cs
command:
(ladebug) alias cs (x) "stop at x; run"
(ladebug) cs(186)
[#2: stop at "x_list.cxx":186 ]
Process has exited
[2] stopped at [int main(void):186 0x120002420]
186 IntNode* newNode = new IntNode(1);
NOTE: No warning is given if the alias_name
already has a
definition as an alias. The old definition will be replaced by the new one.
Use the unalias
command followed by an alias name to delete the
specified alias.
4.7 Executing Shell Commands
You can have the debugger execute a call to the UNIX system
function.
This function is documented in system
(3).
The call results in the sh
shell executing the string you
specify:
execute_shell_command
: sh string
For example, you can execute a system command through a shell from the debugger
by issuing the following command:
(ladebug) sh uname -m
alpha
(ladebug)
To execute more than one command at the specified
shell, spawn a shell as follows:
(ladebug) sh csh -f
% ls out
out
% ls *.b
recio.b
stdio.b
% exit
(ladebug)
You can use the edit
command to invoke the editor defined
by the EDITOR
environment variable:
edit_file_command
: edit [ string ]
The editor is given the string as the file name to edit.
If no file name is specified, the editor is given the current file.
If no current file exists, the editor is started without a file.
If the EDITOR
environment variable is undefined, the debugger
invokes the vi
editor.
The following example invokes the Emacs editor on the file chars.c
:
(ladebug) sh printenv EDITOR
emacs
(ladebug) file
chars.c
(ladebug) edit
The following example invokes the nedit
editor on the file
~/foo/bar.f
:
(ladebug) sh printenv EDITOR
nedit
(ladebug) edit ~/foo/bar.f
This chapter discusses the following topics:
The non-parallel debugger supports debugging multiple processes at a time, but at
any given time is only operating on a single process, known as the current
process. The debugger variable $curprocess
contains the
process id
for this process. Naming and switching the debugger between
processes is described in Multiprocess Debugging.
Debugging Parallel Applications discusses how to use the parallel
debugger to debug multiple processes simultaneously.
The debugger can find and control the following:
- Processes that you may request it to create later
- Processes that are currently running
Specifying an executable file on the shell command line or executing the
load
command causes the debugger to
gain control of a process that you may request it to create later.
NOTE: In the background, the debugger immediately creates a
process executing the program, stalls it, and uses it to answer questions
about which shared libraries are mapped, and so on. This process never
continues, and is killed when:
- The debugger exits
- You unload this executable file
- You try to run the program
Using the run
command on such a potential
process causes the debugger to create a process that is identified as currently
running and recreatable.
Specifying a pid
on the shell command line or executing the
attach
command causes the debugger to
know about the process as currently running and not recreatable.
Catching a fork()
causes the new child process to be
identified as currently running and not recreatable.
Processes contain one or more threads of execution. The threads execute
functions. Functions are sequences of instructions that are generated by compilers from source
lines within source files.
As you enter the debugger commands to manipulate your process, it would be very
tedious to have to repeatedly specify which thread, source file, and so on, you wish
the command to be applied to. To prevent this, each time the debugger stops
the process, it re-establishes a static context and a dynamic context for your
commands. The components of the static context are independent of this run of
your program; the components of the dynamic context are dependent on this run.
Some pieces of these contexts are available as debugger variables.
- The static context consists of the following:
- The dynamic context consists of the following:
- Current call frame
- Current process - $curprocess
- Current thread - $curthread
- The thread executing the event that caused the debugger to gain
control of the process
You can switch most of these individually to point to other instances, as
described in the relevant portions of this manual, and the debugger will modify
the rest of the static and dynamic context to keep the various components consistent.
Chapter 6Running the Program Under Debugger
Control
Often, running the program in a process just requires forking a
process and executing the program within it with the right environment
variables,
argc
/argv
, file descriptors, and so on. This is what
usually happens when you
run your program from a shell command line.
However, sometimes the program requires more context, or a process may already
have been created. Perhaps it is part of a pipe, perhaps it is a long-running process, or perhaps it is created from a shell script or makefile.
Hence, the following situations are possible:
- Running your program as a child process of the debugger process
- Using the debugger's ability to attach to any process it can access
6.1 Running the Program as a Child Process
If your program has a simple command line, and only requires stdin, stdout,
and stderr
connected, you can run it as a child process of the
debugger process. For example:
% ladebug a.out
or
% ladebug
(ladebug) load a.out
6.2 Attaching to a Process
If your program is any of the following,
you can use the debugger's ability to attach to any process to which it has
access:
-
Already running in a process
-
Has a complex command line
-
Is part of a pipe
-
Is started by a script that is difficult to modify
For example:
% ladebug -pid process_id a.out
or
% ladebug
(ladebug) attach process_id a.out
When you do this, the process continues execution until it raises a
signal that
the debugger intercepts, for example,
SEGV. If you have set the $stoponattach
preference variable, it stops immediately.
One method you can use to make attaching to a process work in a predictable way
is to modify your program to loop in a known function until the debugger
interrupts it, for example, when you use Ctrl/C:
- Add some code such as the following to your application:
volatile int endStallForDebugger=0;
void stallForDebugger()
{
while (!endStallForDebugger) ;
}
int main()
{
...
stallForDebugger();
...
}
-
Run this version of your program.
-
Attach the debugger to the running process as described previously.
-
Stop the program with Ctrl/C or by use of
$stoponattach
.
-
Use the debugger to assign to the
stallForDebugger
variable, and continue
the execution of the process, so that it exits from the loop:
(ladebug) assign endStallForDebugger = 1
(ladebug) set any needed breakpoints, and so on
(ladebug) cont
6.3 The load and unload Commands
Using the
load
command, you can tell the debugger which executable
file you intend to execute in some process. The load
command
reads the symbol table information of an executable file and, optionally, a
core file. (This is done automatically when you give the debugger a file name
on the shell
command line.)
load_command
: load filename [ filename ]
For example:
% ladebug /usr/examples/x_list
or:
(ladebug) listobj
Program is not active
(ladebug) load /usr/examples/x_list
Reading symbolic information ...done
(ladebug) listobj
section Start Addr End Addr
------------------------------------------------------------------------------
/usr/examples/x_list
.text 0x120000000 0x120003fff
.data 0x140000000 0x140001fff
/usr/lib/cmplrs/cxx/libcxx.so
.text 0x3ff81f00000 0x3ff81f35fff
.data 0x3ffc1700000 0x3ffc1709fff
/usr/shlib/libexc.so
.text 0x3ff807f0000 0x3ff807f5fff
.data 0x3ffc0210000 0x3ffc0211fff
/usr/shlib/libc.so
.text 0x3ff80080000 0x3ff801c3fff
.data 0x3ffc0080000 0x3ffc0097fff
.bss 0x3ffc0098000 0x3ffc00a3c7f
The second file name is used to specify a core file. If you specify a core file,
the debugger acts as though it is attached
to the process at the point just before it died, except that you cannot execute
commands
that require a runnable process, such as commands that try to continue the
process or evaluate function calls.
Creating a process both creates the debugger's knowledge of it and makes
it the current process that the debugger is controlling.
The opposite of loading an executable file is unloading an executable file:
unload_command
: unload pid ,...
| unload filename
pid
: integer_constant
The unload
command removes all related symbol
table information that the debugger associated with the process being
debugged, specified by either a process id
or an executable file.
For example:
(ladebug) listobj
section Start Addr End Addr
------------------------------------------------------------------------------
/usr/examples/x_list
.text 0x120000000 0x120003fff
.data 0x140000000 0x140001fff
/usr/lib/cmplrs/cxx/libcxx.so
.text 0x3ff81f00000 0x3ff81f35fff
.data 0x3ffc1700000 0x3ffc1709fff
/usr/shlib/libexc.so
.text 0x3ff807f0000 0x3ff807f5fff
.data 0x3ffc0210000 0x3ffc0211fff
/usr/shlib/libc.so
.text 0x3ff80080000 0x3ff801c3fff
.data 0x3ffc0080000 0x3ffc0097fff
.bss 0x3ffc0098000 0x3ffc00a3c7f
(ladebug) unload
Process has exited
(ladebug) listobj
Program is not active
6.4 The run and rerun Commands
After you have loaded a program, you can create a process executing this
program using either of the following forms of the run
command:
run_command
: run [ argument_string ] [ io_redirection ... ]
| rerun [ argument_string ] [ io_redirection ... ]
If the rerun
command is specified without
arguments, the arguments and io_redirection
argument of the most recent
run
command entered with arguments are used. If there was no previous
run
command, the
rerun
command defaults to
run
. If the last modification time or
size of the binary file or any of the shared objects used
by the binary file has changed since the last run
or
rerun
command was issued, the debugger automatically rereads the symbol
table information. If this happens, the old breakpoint
settings may no longer be valid after the new symbol table
information is read.
The argument_string
provides both the argc
and argv
for the
created process in the same way a shell does.
The debugger breaks up the argument_string
into words, and supports several
shell features, including tilde (~) and environment variable expansion, wildcard
substitution, single quote ('), double quote ("), and single character
quote (\).
The io_redirection
argument allows you to change stdin, stdout,
and
stderr
, which are otherwise inherited from the debugger process.
For example:
io_redirection
: < filename
| > filename
| 1> filename
| 2> filename
| >& filename
The various forms have the same effect as in the csh
(1) shell.
NOTE: Although the grammar
currently allows more than the following forms of redirection, only use the following forms because the grammar may change in a future release
of the debugger:
> filename Redirect stdout
1> filename Redirect stdout
2> filename Redirect stderr
>& filename Redirect stdout and stderr
1> filename 2> filename Redirect stdout and stderr to different files
For example:
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run -s > prog.output
[1] stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
6.5 The kill Command
You can kill the current process:
kill_command
: kill
Killing a process leaves the debugger running. Any breakpoints previously
set are retained. You can later rerun
the program.
For example:
(ladebug) show process
Current Process: localhost:614044 (/usr/examples/x_list) paused.
(ladebug) kill
Process has exited
(ladebug) rerun
[1] stopped at [int main(void):182 0x120002418]
182 List<Node> nodeList;
Information: you can restart the execution of your program
from saved positions. Enter "help snapshot" for details.
6.6 The attach Command
If a process already exists, you can have the debugger attach to it:
attach_command
: attach pid [ filename ]
The process is specified by its pid
:
pid
: expression
For example:
(ladebug) attach 12345 a.out
The file name must be an executable file that the process is executing,
or a copy of it, or an unstripped copy of it. If file name is not
specified, the current executable is used.
Attaching to a process both creates the debugger's knowledge of it and
makes it the current process that the debugger is controlling.
When you do this, the process continues execution until it raises a
signal that the debugger intercepts.
Usually you do this by pressing
Ctrl/C or by using the shell command
kill
in another window. Any other mechanism for
raising a signal within the process will also do.
You can set the
debugger variable $stoponattach
to 1 to direct the debugger to immediately stop any process that it attaches to:
(ladebug) ^C
Interrupt (for process)
Stopping process localhost:16077 (loop.out).
Thread received signal INT
stopped at [int main(void):3 0x120001100]
3 while (1) ;
The opposite of attaching to a process is detaching from a process.
When you detach the debugger from a process, all breakpoints are removed and
the process continues to run, but the debugger can no longer identify or
control it:
detach_command
: detach pid ,...
For example:
(ladebug) detach 12345,789
You can set and unset environment variables for processes created in the future
to set up an environment different from the
environment of the debugger and from the shell from which the
debugger was invoked.
When set, the environment variables apply to all new processes you debug.
NOTE: The environment commands have no effect on the environment of any
currently running process. The environment commands do not change or show the
environment variables of the debugger or of the current process. They only
affect the environment variables that will be used when a new process is created.
environment_variable_command
: show_environment_variable_command
| set_environment_variable_command
| unset_environment_variable_command
To print either all the environment variables that are currently set or a
specific one, use a show_environment_variable_command
.
For example:
show_environment_variable_command
: printenv [ environment_variable_name ]
| export
| setenv
NOTE: The
export and
setenv
commands without any arguments are equivalent.
To add or change an environment variable, use a set_environment_variable_command
.
If the environment_variable_value
is not specified, the environment
variable value is set to "".
set_environment_variable_command
: export environment_variable_name = environment_variable_value
| setenv environment_variable_name environment_variable_value
environment_variable_value
: string
For example:
(ladebug) printenv TOOLDIRECTORY
Error: Environment variable 'TOOLDIRECTORY' was not found in the environment.
(ladebug) setenv TOOLDIRECTORY /usr/examples/tools
(ladebug) printenv TOOLDIRECTORY
TOOLDIRECTORY=/usr/examples/tools
To remove an environment variable, use the
unsetenv
command:
unset_environment_variable_command
: unsetenv environment_variable_name
| unsetenv *
If you specify an asterisk (*), all environment variables are removed.
NOTE: There is no command to simply return to the initial
state the environment variables had when the debugger started. You must use
set_environment_variable
commands and
unset_environment_variable
commands
appropriately.
6.8 Multiprocess Debugging
The debugger can find and control more than one process at a time. The debugger
can find and control a process for one of three reasons:
At any one time, you can control only one of the processes that the
debugger controls. The rest are stalled. You must explicitly
switch the debugger to the process you want to work with, stalling the one it
was controlling:
multiprocess_command
: show_process_command
| switch_process_command
You can show the processes the debugger controls:
show_process_command
: show process [ all ]
| process
all
: all
| *
For example:
(ladebug) show process
>localhost:21840 (/usr/examples/x_list) loaded.
You can explicitly command the debugger to control a different process:
switch_process_command
: process pid
| process filename
The process you are switching away from remains stalled until the
debugger exits or until you switch to it and continue it.
The following example creates two processes and switches from one to the
other:
(ladebug) process
There is no current process.
You may start one by using the `load' or `attach' commands.
(ladebug) load /usr/examples/x_list
Reading symbolic information ...done
(ladebug) process
>localhost:21870 (/usr/examples/x_list) loaded.
(ladebug) set $old_process = $curprocess
(ladebug) printf "$old_process=%d", $old_process
$old_process=21870
(ladebug) load /usr/examples/x_segv
Reading symbolic information ...done
(ladebug) process
localhost:21870 (/usr/examples/x_list) loaded.
>localhost:21908 (/usr/examples/x_segv) loaded.
(ladebug) process $old_process
(ladebug) process
>localhost:21870 (/usr/examples/x_list) loaded.
localhost:21908 (/usr/examples/x_segv) loaded.
Both the load
command and
the
attach
command switch
the debugger
to the process on which they operate.
6.9 Processes That Use fork()
The debugger has the following predefined variables that you can set for debugging
a program that forks:
$catchforks
When set to a non-zero value,
this variable instructs
the debugger to stop the child process on exit out of the fork()
or
vfork()
calls. The parent process continues to run. The default is 0 (zero).
$stopparentonfork
When set to a
non-zero value, this variable instructs the debugger to stop the parent process
on exiting out of the fork()
or vfork()
calls after it forks a
child process. The child process
continues to run if $catchforks
is 0; otherwise, it does not.
The default is 0 (zero).
$catchforkinfork
When set to a
non-zero value, this variable
instructs the debugger to stay in the fork routine after the fork and notifies you as
soon as the forked process is created; otherwise, you are notified when the call finishes.
You can debug forking processes before any "atfork" handlers are run by
setting $catchforkinfork
. Because the
target stops inside the system call, you will need to issue up
commands to get
to user-written code. The default is 0 (zero).
When a fork occurs, the debugger sets the debugger variables
$childprocess
and
$parentprocess
to the child and
parent process IDs, respectively.
In the following example, the debugger notifies you that the child process
has stopped. The parent process continues to run.
(ladebug) set $catchforks = 1
(ladebug) run
Process 29027 forked. The child process is 29023.
Process 29023 stopped on fork.
stopped at [int main(void):6 0x120001178]
6 int pid = fork();
fork.c: I am the parent.
Process has exited with status 0
(ladebug) show process
>localhost:29028 (/usr/examples/fork) loaded.
localhost:29023 (/usr/examples/fork) paused.
In the preceding example, note the following:
- The debugger indicates that the child process has stopped, and shows the
line number at which it is stopped.
- The last two lines show that the child process has stopped and that the parent
process has completed execution.
Continuing the previous example, the following shows
how to switch the debugger to the child process. Listing the source
code shows the source for the child process.
(ladebug) process $childprocess
(ladebug) show process
localhost:29028 (/usr/examples/fork) loaded.
>localhost:29023 (/usr/examples/fork) paused.
(ladebug) list
7
8 if (pid == 0)
9 {
10 printf("fork.c: I am the child.\n");
11 }
12 else
13 {
14 printf("fork.c: I am the parent.\n");
15 }
16 }
In the preceding example, note the following:
- The first line switches the current process context to the child process.
- The right angle bracket indicates the current process.
- The
list
command lists the source code for the current process.
NOTE: If you catch the child but not the parent, and the
parent code tries to execute a wait on the child, the target will get stuck if
you do not let the child run to completion. This happens because the parent
is running but making no progress, and the child is stopped by the
debugger. For example:
(ladebug) set $catchforks = 1
(ladebug) set $stopparentonfork = 0
(ladebug) list
10 int new_pid = 0;
11
12 if (pid == 0) {
13 printf( "fork.c: I am the child.\n" );
14 fflush( stdout );
15
16 } else {
17 printf( "fork.c: I am the parent, about to wait.\n" );
18 fflush( stdout );
19
20 new_pid = wait( &status );
21
22 printf( "fork.c: I am the parent, and my wait is finished\n" );
23
24 if (new_pid != pid )
25 printf( "\tthere was some error\n" );
26 else {
27 if (WIFEXITED(status))
28 printf( "\tthe child terminated normally\n" );
29
30 else if (WIFSIGNALED(status))
(ladebug) sh cat ./x.c_fork_hang.txt
If we 'cont' now, the process will fork; the child will be
caught and the parent will run to the 'wait' call and wait
for the child to terminate.
At that time, the child will be under debugger control,
but the current process will be the parent, which will be
running but making no progress. Only a Ctrl/C will allow
further progress.
The example program has set up another process to simulate
a Ctrl/C by the user. It will send SIGINT to the parent.
(ladebug) cont
Process 580893 forked. The child process is 580851.
Process 580851 stopped on fork.
stopped at [void test(void):9 0x120001318]
9 int pid = fork();
fork.c: I am the parent, about to wait.
:
User is waiting here
:
:
Sending SIGINT to parent process
:
Thread received signal INT
stopped at [<opaque> __wait4(...) 0x3ff800d0918]
Information: An <opaque> type was presented during execution of the previous command. For complete type information on this symbol, recompilation of the program will be necessary. Consult the compiler man pages for details on producing full symbol table information using the '-g' (and '-gall' for cxx) flags.
(ladebug) where
>0 0x3ff800d0918 in __wait4(...) in /usr/shlib/libc.so
#1 0x3ff800d668c in __wait(...) in /usr/shlib/libc.so
#2 0x120001398 in test() "c_fork_hang.c":20
#3 0x120001528 in main() "c_fork_hang.c":71
#4 0x1200012a8 in __start(...) in /usr/examples/c_fork_hang
(ladebug) show process
>localhost:580893 (/usr/examples/c_fork_hang) paused.
\_localhost:580851 (/usr/examples/c_fork_hang) paused.
6.10 Processes That Use exec()
Set $catchexecs
to 1 to instruct the debugger to stop
the process and
notify you when an exec
occurs. The process stops before executing any user
program code or
static initializations. You can debug the newly executed process.
The debugger keeps a history of the progression of
the executed files.
In the following scenario, you set the predefined variables
$catchforks
and
$catchexecs
to 1. The debugger will notify
you when an execution occurs.
Because $catchforks
is set, you will also be tracking the child
process and, therefore, you will be notified of any exec
in the child
process.
The following example shows an exec
occurring on the current
context and the child process stopped on the run-time loader entry point:
(ladebug) set $catchforks = 1
(ladebug) set $catchexecs = 1
(ladebug) run
Process 14839 forked. The child process is 14835.
Process 14835 stopped on fork.
stopped at [int main(void):8 0x1200011f8]
8 if ((pid = fork()) == 0)
x_exec.c: I am the parent.
Process has exited with status 0
(ladebug) show process
>localhost:14918 (x_exec) loaded.
localhost:14835 (x_exec) paused.
(ladebug) process $childprocess
(ladebug) list 6: 13
6 int pid;
7
> 8 if ((pid = fork()) == 0)
9 {
10 printf("About to exec \n");
11 fflush(stdout); /* Make sure the output gets out! */
12 execlp("announcer", "announcer", NULL);
13 printf("After exec \n");
14 }
15 else
16 {
17 printf("x_exec.c: I am the parent.\n");
18 }
(ladebug) cont
About to exec
The process 14835 has execed the image "./announcer".
Reading symbolic information ...done
stopped at [ 0x3ff8001bf48]
5 printf("announcer.c: I am here!! \n");
Note the following:
- Use
process $childprocess
to set the current process context to the child process.
- Listing the source code, you can see the process is almost ready to execute.
- The debugger notifies you when the
exec
occurs.
- The child process is stopped on the run-time loader entry point. The source display shows the code
in the main routine.
6.11 Core File Debugging
When the operating system encounters an unrecoverable error, for example, a
segmentation violation (SEGV),
the system creates a file named core
and places it in the current directory.
The core file is not an executable file; it is a snapshot of the state of
your process at the time the error occurred. It allows you to analyze the
process at the point it crashed. For more information on core file debugging,
see Core File Debugging in Part III.
6.12 Kernel Debugging
The debugger supports kernel debugging, which is a task normally performed by
systems engineers or system administrators. Debugging the kernel is a
specialized process and can take
several forms. This debugger supports local and remote kernel debugging, and
crash dump analysis. For more information on kernel
debugging, see Kernel Debugging in Part III.
To determine why a problem is happening, you usually want to execute your
program up to or just before the point at which you observe the first evidence
of the problem. Then you can examine the internal state of your program and
try to identify something that explains the visible problem. Possibly you
will see right away how the problem occurs, in which case you are finished
debugging. You then correct your program, recompile, relink, and confirm that
the correction works as intended.
Often, you will see something about the program
state that is wrong, but you will not see how it got that way. In that case, you need to
make a guess at where the mistake might have occurred. Then, repeat this
whole process, trying to stop at or just before the possible trouble point.
For simple problems, it may be easy to describe the
conditions under which you want to stop the program; for example, "the
first time traverse
is called" or "when division_by_zero
occurs." Other
situations may require either more complex descriptions or repeated
trial-and-error attempts to discover the critical information needed to solve
your problem.
Breakpoints provide the means by which you specify to the debugger an event
or condition
under which you want to intervene in the execution of your program and what
actions you want the debugger to take when that event is detected.
You can define breakpoints based on the following actions:
- Reaching a certain place in your
program (such as entering a certain function or reaching code for a particular
source line number)
- Accessing the contents of a variable or other memory when it is either
read or written
- Raising a specified signal
You can also enable, disable, or delete breakpoints.
Breakpoint commands include the following:
breakpoint_command
: breakpoint_definition_command
| simple_stop_command
| signal_command
| obsolete_breakpoint_definition_command
| breakpoint_table_command
This chapter discusses the following topics:
The following is a particularly common breakpoint:
(ladebug) stop in main
[#1: stop in int main(void) ]
This command tells the debugger that when execution enters the function
main
, you want the debugger to suspend execution and return control
to you.
The debugger responds to a breakpoint command by displaying how it
recorded the request internally.
The debugger assigns a number to the breakpoint (in this case, it is 1), which
it uses later to
refer to that breakpoint. The debugger does not just repeat the command as you entered it;
it provides a more complete description of the function main
to help you
confirm that it has correctly identified the function you meant.
Later, after you cause the program to execute, if that event
occurs, the debugger reports the event and then prompts you for
what to do next. For example:
(ladebug) run
[1] stopped at [int main(void):182 0x120002418]
182 List<Node> nodeList;
Both the event part and the action part of a breakpoint definition command
consist of several subparts:
breakpoint_definition_command
: disposition
[ quiet ]
detector
[ thread_filter ]
[ logical_filter ]
[ breakpoint_actions ]
where the detector
,
thread_filter
(if specified),
and logical_filter
(if
specified) collectively specify the event part, and the
disposition, quiet
(if specified) and
breakpoint_actions
(if specified) collectively specify the action part.
NOTE: Additional obsolete forms of breakpoint definition
are retained only for backward compatibility with earlier versions of the debugger.
These forms are explained in
Obsolete Breakpoint Commands.
The obsolete forms may be eliminated in a future release.
There are three distinct points in time at which a breakpoint definition
has an effect:
- When the command is entered
The command is parsed, names and expressions that occur in any of the
event parts are evaluated, and the breakpoint actions are parsed and checked for
correctness (but not evaluated).
- When the debugger initiates program execution
For each breakpoint that is not disabled, appropriate modifications are
made to the program to enable detection of the
specified event.
- When a detector triggers during program execution
The thread filter specification (if present) and logical filter (if present)
are evaluated to determine whether the breakpoint as a whole has triggered.
If not, then execution is resumed (silently). If so, the breakpoint actions are
performed, after which execution stops or resumes according to the specified
disposition.
disposition
: stop
| when
The stop
command specifies that when the event specified by the
breakpoint occurs and all processing for that breakpoint has been completed,
the debugger should prompt for further commands.
The when
command specifies that when the event specified by the
breakpoint occurs and all processing for that breakpoint has been completed,
the debugger may resume execution of the program. See the section
When Multiple Breakpoints Trigger at Once
for an explanation of how the debugger determines when to resume execution.
By default, when an event is detected and the debugger determines that the
breakpoint actions should be performed, the debugger prints a line that
identifies the breakpoint, for example:
(ladebug) when in main { stop }
[#1: when in int main(void) { stop } ]
(ladebug) run
[1] when [int main(void):182 0x120002418]
[1] stopped at [int main(void):182 0x120002418]
182 List<Node> nodeList;
The optional quiet
specifier tells the debugger to omit this
information, as in the following example:
(ladebug) when quiet in main { stop }
[#11: when quiet in int main(void) { stop } ]
(ladebug) run
(ladebug) list $curline: 1
> 182 List<Node> nodeList;
The debugger uses several kinds of detectors, each corresponding to a particular
kind of event:
detector
: place_detector
| watch_detector
| signal_detector
| unaligned_detector
A place detector specifies a place or location in your program. It can
refer
to the beginning of a function, a particular line in one of your source files,
a specific value of the PC (program counter), or certain sets of these.
A watch detector specifies a variable or other memory locations that
should be monitored to detect certain kinds of access (read, write, and so
on).
A signal detector specifies a set of UNIX signals to be monitored.
An unaligned access detector specifies any kind of memory access using an unaligned
access.
This section describes each type of detector.
7.1.3.1 Place Detectors
You can use place detectors to determine when execution reaches a particular
place or location in your program:
place_detector
: in function_name
| in all function_name
| pc address_expression
| at line_specifier
| every proc entry
| every procedure entry
| every instruction
| expression
The in
function_name
detector
specifies the event at which execution reaches the entry of the
named function.
If the function name is ambiguous (more than one
function can match the name in some languages, including C++), the debugger
prompts you with a list of alternatives from which to choose:
(ladebug) stop in foo
Select from
----------------------------------------------------
1 int C::foo(double*)
2 void C::foo(float)
3 void C::foo(int)
4 void C::foo(void)
5 None of the above
----------------------------------------------------
2
[#4: stop in void C::foo(float) ]
If you choose the last option (None of the above), then no function is
selected and no breakpoint is defined.
The in all
function_name
detector
is the same as in
function_name
except that it
specifies all of the functions that match the given name, whether one or more:
(ladebug) stop in all foo
[#3: stop in all foo ]
The pc
address_expression
detector
specifies the event at which execution reaches the given machine address:
(ladebug) stop pc 0x120002498
[#7: stop PC == 0x120002498 ]
The at
line_specifier
detector
specifies the event at which code associated with a particular
line of the source is reached:
(ladebug) stop at 190
[#8: stop at "x_list.cxx":190 ]
If no code is associated with the given line number, the debugger
finds and substitutes the closest higher line number that has
associated code.
The every procedure entry
detector specifies that a
breakpoint should be
established for every function entry point in the program.
(ladebug) stop every procedure entry
[#9: stop every procedure entry ]
NOTE: This command can be very time consuming because it searches
your entire program including all shared libraries that it references
and establishes breakpoints for every entry point in every executable image.
This can also considerably slow execution of your program as it runs.
A disadvantage of this command is that it establishes breakpoints for hundreds
or even thousands of entry points about which you have little or no information.
For example, if you use stop every proc entry
immediately
after loading a program and then run it, the debugger will stop or
trace over 100 entry points before reaching your main entry point.
About the only thing that you can do if execution stops at most such unknown
places is continue until some function relevant to
your debugging is reached.
The every instruction
detector specifies a breakpoint for every
instruction in your entire program:
(ladebug) stop every instruction
[#10: stop every instruction ]
When used with the stop
disposition, a subsequent continue
behaves essentially the same
as a step by instruction command (see stepi
).
When used with the when
disposition, subsequent
next
and step
commands allow you to trace
all of the instructions that are executed as a result of those stepping
commands.
Be aware that even when next
is used to step over a called
routine, the trace output includes all of the instructions that are executed
within the called routine (and any routines that it calls).
NOTE: This
command will slow execution of your program considerably.
The detector expression
(that is, an expression not preceded
by one of the keywords in
, at
, or
pc
) specifies either a function name or line number,
depending on how the expression is parsed and evaluated. An expression
that evaluates to the name of a function is handled just like the
equivalent command that uses in
in the detector; otherwise,
it is handled like the equivalent command that uses at
in the detector.
7.1.3.2 Watch Detectors
You can use watch detectors to determine when a variable or other memory
location is read or written and/or changed. Breakpoints with watch detectors are
also known as watchpoints.
watch_detector
: basic_watch_detector watch_detector_modifiers
basic_watch_detector
: variable expression
| memory start_address_expression
| memory start_address_expression , end_address_expression
| memory start_address_expression : byte_count_expression
watch_detector_modifiers
: [ access_modifier ] [ within_modifier ]
access_modifier
: write
| read
| changed
| any
within_modifier
: within function_name
You can specify a variable whose memory is to be watched, or specify the
memory directly.
The accesses that are considered can be limited to those
that write (the default), read, write and actually change the value, or
can include all accesses.
If you specify a variable, the memory to be watched includes all of the
memory for that variable, as determined by the variable's type. The following
example watches for write access to variable _nextNode
,
which is allocated in the 8 bytes at the address shown in the last line of
the example:
(ladebug) whatis _nextNode
class Node* Node::_nextNode
(ladebug) print sizeof((_nextNode))
8
(ladebug) stop variable _nextNode write
[#3: stop variable _nextNode write ]
The specified variable is watched. If "p" is a pointer,
watch variable p
will watch the content of
the pointer, not the memory pointed to by "p". Use
watch memory *p
to watch the memory pointed
to by "p", as well as watch variable *p
.
If you specify memory directly in terms of its address, the
memory to be watched is defined as follows:
- If an end address is given, then all bytes of memory from the start
address to and including the end address:
(ladebug) stop memory 0x140001800, 0x140001803 read
[#5: stop memory 0x140001800, 0x140001803 read ]
This watches the 4 bytes specified on the command line.
- If you specify a byte count, then the given number of bytes starting at
the given start address:
(ladebug) stop memory 0x140001800 : 2 changed
[#6: stop memory 0x140001800 : 2 changed ]
This watches the 2 bytes specified on the command line for a change
in contents.
If you specify the within
modifier, then only those
accesses that occur
within the given function (but not any function it calls)
are watched. For example:
(ladebug) whatis t
int t
(ladebug) stop variable t write within foo
[#2: stop variable t write within void C::foo(void) ]
(ladebug) cont
[2] Address 0x140000248 was accessed at:
void C::foo(void): x_overload.cxx
[line 22, 0x12000195c] stl r31, 0(r2)
0x140000248: Old value = 0x0000000f
0x140000248: New value = 0x00000000
[2] stopped at [void C::foo(void):22 0x120001960]
22 void C::foo() { t = 0; state++; return; }
7.1.3.3 Signal Detectors
You can use signal detectors to determine when a particular UNIX signal is raised:
signal_detector
: signal signal_id ,...
signal_id
: integer_constant
| signal_name
You can specify signals by numeric value or by their conventional UNIX
names, without or without the leading "SIG":
(ladebug) stop signal SEGV, 3, SIGINT
[#2: stop signal SEGV, 3, SIGINT ]
If the debugger catches a signal event, then a subsequent simple
continue
will resume execution without
raising the signal again in your process. However, you can specify a signal
as part of the continue
command to send the signal to
your process when it resumes.
7.1.3.4 Unaligned Access Detectors (Tru64 UNIX Only)
You can use an unaligned access detector to determine when an unaligned memory
access occurs:
unaligned_detector
: unaligned
Unaligned accesses are automatically handled by the Tru64 UNIX operating
system. By default, an unaligned access results in an information message
and then is corrected so that your program can continue. (You or your system
administrator can choose a different default. See uac
(1) for
more information.) This message looks like this:
Unaligned access pid=30231 va=0x11ffff791 pc=0x120001af4 ra=0x120001b84 inst=0xa0220000
You can request the debugger to detect unaligned accesses:
(ladebug) stop unaligned
[#1: stop unaligned ]
(ladebug) run
Thread encountered Unaligned Access
[1] stopped at [int unalignedAccess(void):27 0x120001af8]
27 return y;
7.1.3.5 Unaligned Access Detector (Linux Only)
Unaligned accesses are automatically handled and quietly corrected on Linux.
The debugger cannot detect these events.
A thread filter determines whether a detected event should be further
considered for breakpoint processing:
thread_filter
: thread thread_id ,...
The thread_id
expressions are evaluated at the time the breakpoint command
is entered, and each must yield an integer value.
A detected event is retained for further consideration only if
the thread in which the event occurs matches one of the given
threads.
If not, the detection is quietly ignored.
If the thread_filter
does not indicate a match, then any
related logical filter is not evaluated.
A logical filter determines whether a detected event should be further
considered for breakpoint processing:
logical_filter
: if expression
A detected event is retained for further consideration only if
the given expression evaluates to true
. If not, the
detection is quietly ignored.
The expression is checked syntactically in the context of the place where the
breakpoint command
is given: it must be syntactically valid according to the language rules that
apply there. However, the expression is not evaluated and
names that occur in the expression need not be visible. After the syntax
check, the expression is remembered in an internal form and is not rechecked
later when it is evaluated.
If an error occurs when the expression is evaluated, for example, because
a name in the expression is not defined, then the error is reported and
the value of the expression is assumed to be true
.
An error in the expression does not change the disposition.
If continuation was specified, then that is still what occurs. For example:
(ladebug) when in List<Node>::append if x
[#5: when in void List<Node>::append(class Node* const) if x ]
(ladebug) cont
Symbol "x" is not defined.
[Error while evaluating breakpoint condition - taken as true]
[5] when [void List<Node>::append(class Node* const):148 0x120001dbc]
Symbol "x" is not defined.
[Error while evaluating breakpoint condition - taken as true]
[5] when [void List<Node>::append(class Node* const):148 0x120001dbc]
[4] stopped at [int main(void):195 0x120002600]
195 nodeList.append(new IntNode(3));
It is valid for a logical filter expression to contain a call to another
routine in your program. Such a call is evaluated in the same way as if
it occurred in a call
or print
command.
However,
execution of the called routine might result in triggering a breakpoint;
this is called a recursive breakpoint.
The action part of a breakpoint command specifies actions to be
performed when the event part has triggered (including passing any
thread and/or logical filters):
breakpoint_actions
: { action_list }
action_list
: command
| command ;
| command ;...
7.1.6.1 Special Commands
The following debugger commands behave differently in some fashion when used within
a breakpoint action list:
- Simple stop
A simple_stop_command
is a stop without any
detector or other parameters:
simple_stop_command
: stop
If used within a breakpoint action list, it specifies
that the disposition for the breakpoint should be to stop after completion
of action list processing, even if the breakpoint was specified with the
when
disposition. If used outside an action list, it
has no effect.
A simple stop command does not terminate action list processing;
it only affects the disposition that applies later. For example:
(ladebug) when in List<Node>::print { stop ; print "*** stopped ***"}
[#6: when in void List<Node>::print(void) { stop ; print "*** stopped ***"} ]
(ladebug) cont
[6] when [void List<Node>::print(void):162 0x120001e90]
*** stopped ***
[6] stopped at [void List<Node>::print(void):162 0x120001e90]
162 Node* currentNode = _firstNode;
- History
The history
command does not display commands that
are performed as part of the action list of a breakpoint.
7.1.6.2 Commands to Use with Caution
You must be careful when using some commands in breakpoint
action lists. The following commands cause the debugger to resume execution
of your program in the midst of action list processing:
call
continue
goto
next
return
step
- Any command that contains an expression whose evaluation involves
calling a function in your program
It is easy in such cases to lose track
of just what state breakpoint processing is really in or where you
really are in your program. Such confusion may mislead or misdirect your
debugging effort. For further discussion, see the section
Recursive Breakpoints.
7.1.6.3 Commands to Avoid
You should avoid altogether some commands in breakpoint
action lists. The following are commands that directly or indirectly change the
process that the debugger is controlling:
attach
and detach
run
and rerun
process
with an argument
The debugger does not explicitly prohibit these commands, but their behavior
within action lists is implementation-defined and subject to change from
release to release. In specialized cases, you may be able to obtain
useful results by using them in action lists, but do not expect the same
behavior over the long term.
It is possible for multiple breakpoints to specify the same event, or possibly
overlapping events. Thus, more that one breakpoint detector may trigger at the
same time.
When more than one breakpoint detector triggers, the thread filters and
logical filters of all the breakpoints involved are processed before
the action part of any breakpoint is performed.
After the set of breakpoints that trigger is determined, the action
parts of each of them are performed in an undefined order.
After all action parts are performed, execution of the program is resumed
only if all of the breakpoints so specify in their disposition.
If any one of them
specifies a break, the debugger prompts you for further commands.
The following commands cause the debugger to resume execution
of your program while in the midst of action list processing:
call
continue
goto
next
return
step
- Any command that contains an expression whose evaluation involves
calling a function in your program
In all of these cases, the debugger temporarily suspends processing of the
current breakpoint to start your program executing again and then waits
for that execution to complete. As long as no new breakpoint is triggered
during that execution, all will be fine. However, if a new breakpoint
triggers, in particular one with the stop
disposition,
then you may be prompted for new command input for the recursive
breakpoint even before the initial breakpoint has completed. Further,
continuing execution may ultimately allow the original breakpoint to
complete, at which time its disposition will come into play.
It is easy in such cases to lose track
of just what state breakpoint processing is really in or where you
really are in your program. Such confusion may mislead or misdirect your
debugging effort. See the
call
command
example, which shows suspended execution in nested function calls.
This section describes how to use breakpoints when debugging C++ programs.
7.1.9.1 Member Functions
The following program illustrates setting breakpoints in C++ member functions:
(ladebug) list 3: 25
3 class C {
4 public:
5 void foo();
6 void foo(int);
7 void foo(float);
8 int foo(double *);
9 };
10
11 C o;
12 C* p = new C;
13 int t = 0;
14 int state = 1;
15
16 main(){
17 t++;
18 o.foo();
19
20 }
21
22 void C::foo() { t = 0; state++; return; }
23 void C::foo(int i) { state++; return; }
24 void C::foo(float f) { state++; return; }
25 int C::foo(double *) { return state;}
You must name member functions in a way that makes them visible at the
current position, according to the normal C++ visibility rules. For example:
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run
[1] stopped at [int main(void):17 0x120001924]
17 t++;
(ladebug) stop in foo
Symbol "foo" is not defined.
foo has no valid breakpoint address
Warning: Breakpoint not set
If not positioned within a member function of a class, it is generally necessary
to name the desired member function using type qualification, an object of
the class type, or a pointer to an object of the class type. For example:
(ladebug) stop in C::foo
Select from
----------------------------------------------------
1 int C::foo(double*)
2 void C::foo(float)
3 void C::foo(int)
4 void C::foo(void)
5 None of the above
----------------------------------------------------
3
[#5: stop in void C::foo(int) ]
(ladebug) stop in o.foo
Select from
----------------------------------------------------
1 int C::foo(double*)
2 void C::foo(float)
3 void C::foo(int)
4 void C::foo(void)
5 None of the above
----------------------------------------------------
1
[#6: stop in int C::foo(double*) ]
(ladebug) stop in p->foo
Select from
----------------------------------------------------
1 int C::foo(double*)
2 void C::foo(float)
3 void C::foo(int)
4 void C::foo(void)
5 None of the above
----------------------------------------------------
4
[#7: stop in void C::foo(void) ]
You can avoid the ambiguity associated with an overloaded function by
specifying a complete signature for the function name. For example:
(ladebug) stop in C::foo(void)
[#8: stop in void C::foo(void) ]
(ladebug) stop in C::foo(int)
[#9: stop in void C::foo(int) ]
7.1.9.2 Templates and Instantiations
The debugger has no knowledge of templates that may occur in your program.
However, you can usually debug template instantiations the same way as
the equivalent non-instantiated class or function.
The following source text illustrates debugging of template instantiations:
(ladebug) list 144: 13
144 template <class NODETYPE>
145 void List<NODETYPE>::append(NODETYPE* const node)
146 {
147
148 if (!_firstNode)
149 _firstNode = node;
150 else {
151 Node* currentNode = _firstNode;
152 while (currentNode->getNextNode())
153 currentNode = currentNode->getNextNode();
154 currentNode->setNextNode(node);
155 }
156 }
Normal debugging commands then apply to the instantiation (not the template
as such):
(ladebug) whatis List<Node>::append
void List<Node>::append(class Node* const)
(ladebug) stop in List<Node>::append
[#1: stop in void List<Node>::append(class Node* const) ]
(ladebug) run
[1] stopped at [void List<Node>::append(class Node* const):148 0x120001d9c]
148 if (!_firstNode)
(ladebug) where 2
>0 0x120001d9c in ((List<Node>*)0x11fffee68)->List<Node>::append(node=0x140002c00) "x_list.cxx":148
#1 0x1200024a4 in main() "x_list.cxx":187
7.1.9.3 Exception Handlers
When working with exception handlers, you can set a breakpoint at the
appropriate line to determine if an exception is thrown. In addition,
you can set breakpoints in the following functions, which are part of the C++
library support for exceptions:
terminate |
Gains control when any unhandled exception occurs, which will result
in program termination. |
unexpected |
Gains control when a function containing an exception specification
tries to throw an exception that is not included in that specification. |
The following source code illustrates these special library functions:
(ladebug) list 30: 29
30 // Throw an exception. The "throw(int)" syntax tells the compiler that
31 // only integer exceptions can escape this method. This will result in
32 // an unexpected exception from C++.
33 //
34 void throwAnException() throw(int)
35 {
36 throw "Bug";
37 }
38
39 // Provide some depth to the stack, for demonstration purposes
40 //
41 void someOperation()
42 {
43 int z = unalignedAccess(); // Some tests ignore this exception
44 throwAnException();
45 }
46
47 main()
48 {
49 try {
50 someOperation();
51 }
52 catch(char* str) {
53 cout << "Caught exception [" << str << "]" << endl;
54 }
55 catch(...) {
56 cout << "Caught something" << endl;
57 }
58 }
You can trace the flow of execution, as in the following:
(ladebug) stop at 52
[#1: stop at "x_signals.cxx":52 ]
(ladebug) stop in all terminate
[#2: stop in all terminate ]
(ladebug) stop in all unexpected
[#3: stop in all unexpected ]
(ladebug) run
[3] stopped at [<opaque> unexpected(void) 0x3ff802a064c]
Information: An <opaque> type was presented during execution of the previous command. For complete type information on this symbol, recompilation of the program will be necessary. Consult the compiler man pages for details on producing full symbol table information using the '-g' (and '-gall' for cxx) flags.
(ladebug) where
>0 0x3ff802a064c in unexpected(...) in /usr/lib/cmplrs/cxx/libcxx.so
#1 0x120001b38 in throwAnException() "x_signals.cxx":36
#2 0x120001b88 in someOperation() "x_signals.cxx":44
#3 0x120001bc4 in main() "x_signals.cxx":50
#4 0x1200019c8 in __start(...) in /usr/examples/x_signals
(ladebug) cont
[3] stopped at [<opaque> unexpected(...) 0x3ff80287704]
(ladebug) where
>0 0x3ff80287704 in unexpected(...) in /usr/lib/cmplrs/cxx/libcxx.so
#1 0x3ff802a064c in unexpected(...) in /usr/lib/cmplrs/cxx/libcxx.so
#2 0x120001b38 in throwAnException() "x_signals.cxx":36
#3 0x120001b88 in someOperation() "x_signals.cxx":44
#4 0x120001bc4 in main() "x_signals.cxx":50
#5 0x1200019c8 in __start(...) in /usr/examples/x_signals
(ladebug) cont
[2] stopped at [<opaque> terminate(...) 0x3ff802875cc]
(ladebug) where
>0 0x3ff802875cc in terminate(...) in /usr/lib/cmplrs/cxx/libcxx.so
#1 0x3ff80287750 in unexpected(...) in /usr/lib/cmplrs/cxx/libcxx.so
#2 0x3ff802a064c in unexpected(...) in /usr/lib/cmplrs/cxx/libcxx.so
#3 0x120001b38 in throwAnException() "x_signals.cxx":36
#4 0x120001b88 in someOperation() "x_signals.cxx":44
#5 0x120001bc4 in main() "x_signals.cxx":50
#6 0x1200019c8 in __start(...) in /usr/examples/x_signals
(ladebug) cont
Thread received signal ABRT
stopped at [<opaque> __kill(...) 0x3ff800e1578]
UNIX signals are operating-system-defined events that can be handled by
the debugger. This section discusses breakpoint commands for UNIX signal events.
It also discusses unaligned accesses and using Ctrl/C to interrupt your program.
7.1.10.1 The catch and ignore Commands
You can use two special breakpoint commands, catch
and
ignore
, to handle UNIX signal events:
signal_command
: catch_command
| ignore_command
catch_command
: catch [ signal_id ]
ignore_command
: ignore [ signal_id ]
A catch
command with an operand specifies that the debugger should
catch and handle the given UNIX signal. You can specify the signal by integer
number or by standard signal name, with or without the leading "SIG". The
catch
command is equivalent to the breakpoint command:
(ladebug) catch BUS
or
(ladebug) stop signal SIGBUS
[#1: stop signal SIGBUS ]
with these exceptions:
- No entry is made in the breakpoint table for a
catch
command.
- A catch for a signal that is already being caught does not create
an additional breakpoint for that signal.
An ignore
command with an operand specifies that the given UNIX signal
should
not be caught or handled by the debugger; rather, such a signal is passed to your
program. The ignore
command is equivalent to deleting the
breakpoint created by a catch
command for that
signal:
(ladebug) ignore BUS
A catch
command without an operand lists all signals that are currently
being handled. Similarly, an ignore
command without an operand lists the
signals that are currently being ignored. Together, the two lists show
all signals known to the debugger.
You can issue these commands immediately after
the debugger starts to show which signals are caught and which are ignored by
default:
(ladebug) catch
INT, QUIT, ILL, TRAP, ABRT, EMT, FPE, BUS, SEGV, SYS, PIPE, TERM, URG, STOP, TTIN, TTOU, XCPU, XFSZ, PROF, USR1, USR2, VTALRM, RTMIN, RTMIN1, RTMIN2, RTMIN3, RTMIN4, RTMIN5, RTMIN6, RTMIN7, RTMAX, RTMAX7, RTMAX6, RTMAX5, RTMAX4, RTMAX3, RTMAX2, RTMAX1
(ladebug) ignore
HUP, KILL, ALRM, TSTP, CONT, CHLD, WINCH, IO
NOTE:: Signals RTMIN, RTMIN1,...,RTMIN7, RTMAX, and RTMAX7,...,RTMAX1
apply only on Tru64 UNIX.
7.1.10.2 Unaligned Accesses (Tru64 UNIX Only)
You can request the debugger to catch unaligned accesses:
(ladebug) catch unaligned
This command is very much like the stop unaligned
command.
Although this looks like a normal catch
command, it
differs in several respects:
unaligned
is not the name of a UNIX signal.
- There is no corresponding signal number.
unaligned
is never listed by either the
catch
or ignore
commands without
an argument.
Like other catch
commands, the following rules apply:
- No entry is made in the breakpoint table for a
catch
command.
- Repeating the command does not create an additional breakpoint.
NOTE: You cannot specify unaligned
in a signal
detector of a normal breakpoint definition.
You can request the debugger to ignore unaligned accesses when
catch unaligned
is in effect (the default) by using the following
command:
(ladebug) ignore unaligned
However, if a breakpoint was defined using an
unaligned access detector, then it must
be disabled using a disable or
delete breakpoint command.
7.1.10.3 Unaligned Accesses (Linux Only)
Unaligned accesses are automatically handled and quietly corrected on Linux.
The debugger cannot catch these events.
7.1.10.4 Ctrl/C
If your program seems to be caught in a loop, you can press Ctrl/C.
The debugger interprets this as a command to send a signal interrupt (SIGINT
)
to your program. Because the debugger itself catches signal SIGINT
in the
target program by default, this interrupts your program and returns control to the Ladebug
prompt.
If you give the command ignore SIGINT
, then it is no longer
possible to regain control of your program using Ctrl/C. In that case,
signal
SIGINT
is delivered directly to your program. Unless your program
has explicitly arranged otherwise, SIGINT
will result in program
termination.
A process starts with a copy of its parent's memory as the result of a
fork()
system call; after running for a while within that
memory, the process will often make an exec()
system call to
start a new executable file within that process.
The debugger keeps track of the exec()
calls that occur so that it can
keep track of various properties associated with each executable file. In
particular, the breakpoint table is one of those properties. Thus, if you
run
or rerun
your program, the same breakpoints
can be re-established, even though a new process is initiated. Similarly,
if you work with more than one process, each process has a distinct breakpoint
table associated with it.
When a dlopen()
system call occurs, the debugger reprocesses the current
breakpoint table and automatically sets up the means to detect any events
that apply to the newly loaded image.
When a dlclose()
system call occurs, the debugger also reprocesses the
breakpoint and de-activates any events that apply to the unloaded image.
The following forms of breakpoint commands are obsolete, but are still
supported for backward compatibilty with earlier versions of the debugger:
obsolete_breakpoint_definition_command
: obsolete_watch_breakpoint_definition_command
| obsolete_trace_breakpoint_definition_command
| obsolete_stopi_breakpoint_definition_command
| obsolete_wheni_breakpoint_definition_command
| obsolete_tracei_breakpoint_definition_command
7.1.12.1 Obsolete Watchpoint Definition
An obsolete watchpoint definition is similar to a stop variable
or stop memory
breakpoint:
obsolete_watch_breakpoint_definition_command
: watch obsolete_watch_detector
[ obsolete_watch_modifiers ]
[ breakpoint_actions ]
obsolete_watch_detector
: variable variable_name
| [ memory ] start_address_expression
| [ memory ] start_address_expression , end_address_expression
| [ memory ] start_address_expression : byte_count_expression
obsolete_watch_modifiers
: [ access_modifier ]
[ thread_filter ]
[ within_modifier ]
[ logical_filter ]
An obsolete watchpoint and a stop
command differ in the following
respects:
- The obsolete watchpoint command begins with
watch
instead of
stop
.
- The keyword
memory
is optional; if omitted, it is assumed.
- The order of filters and modifiers is different.
These differences are purely syntactic; the semantics are the same.
(ladebug) watch variable _firstNode write
[#3: watch variable _firstNode write ]
(ladebug) cont
[3] Address 0x11fffbf91 was accessed at:
void List<Node>::append(class Node* const): x_list.cxx
[line 149, 0x120001dd0] stq r2, 0(r3)
0x11fffbf90: Old value = 0x0000000000000000
0x11fffbf90: New value = 0x0000000140002c00
[3] stopped at [void List<Node>::append(class Node* const):149 0x120001dd4]
149 _firstNode = node;
7.1.12.2 Obsolete Tracepoint Definition
An obsolete tracepoint definition is similar to a when in
or when at
breakpoint, possibly combined with watching
for a change of a variable's value:
obsolete_trace_breakpoint_definition_command
: trace [ variable_name ]
[ thread_filter ]
[ where_modifier ]
[ logical_filter ]
[ breakpoint_actions ]
| trace function_name [ logical_filter ] [ breakpoint_actions ]
| trace line_specifier [ logical_filter ] [ breakpoint_actions ]
where_modifier
: in function_name
| at line_specifier
line_specifier
: filename:line_number
| line_number
Following are the differences between an obsolete tracepoint and a when
command:
- The obsolete tracepoint command begins with
trace
instead of
when
.
- If you specify a variable name, a trace identification line is
displayed only when the value of the variable changes (and the logical
filter evaluates to
true
).
The debugger implementation of trace
for
detecting variable changes tends
to be slowat each place where control might be stopped, as specified by
the where
modifier and filters, the value of the variable is compared to
the value remembered at the time execution began.
- The order of filters and modifiers is different.
For example:
(ladebug) trace in List<Node>::print
[#7: trace in void List<Node>::print(void) ]
(ladebug) trace i in List<Node>::print
[#8: trace i in void List<Node>::print(void) ]
(ladebug) trace List<Node>::print if i { print "Test 1"}
[#9: trace in void List<Node>::print(void) if i { print "Test 1"} ]
If the trace
command is given with no arguments, the debugger
prints a trace identification line when each function in your program is
entered. For example:
(ladebug) trace
[#10: trace ]
(ladebug) status
#10 at procedure entry { trace-proc }
This is equivalent to the when every proc entry
command
(with equivalent performance degradation).
7.1.12.3 Instruction-Related Breakpoint Commands
The following commands control obsolete instruction-related breakpoints:
obsolete_stopi_breakpoint_definition_command
: stopi [ expression ]
[ thread_filter ] [ match_address ] [ logical_filter ]
obsolete_tracei_breakpoint_definition_command
: tracei [ expression ]
[ thread_filter ] [ match_address ] [ logical_filter ]
obsolete_wheni_breakpoint_definition_command
: wheni [ expression ]
[ thread_filter ] [ match_address ] [ logical_filter ]
breakpoint_actions
match_address
: at address_expression
The stopi
, tracei
, and wheni
forms of breakpoint definition are similar to the corresponding
stop
, trace
,
and when
forms,
with the following differences:
- They have a different initial keyword.
- If you specify a variable name, then the breakpoint triggers
only when the value of the variable changes (and the logical and thread
filters are true).
The debugger implementation of tracei
for detecting variable changes tends
to be slow: at each place where control might be stopped, as specified by
the where
modifier and filters, the value of the variable is compared to
the value remembered at the time execution began.
Most important, the variable change and filter tests are performed
after every instruction is executed, making these
definitions especially demanding on program performance.
- The order of filters and modifiers is different.
- The
at
keyword is followed by an address in these commands,
instead of by a line number.
As breakpoints are defined, they are recorded in a breakpoint table
associated with the current program. You can display and modify this table
in certain limited ways:
breakpoint_table_command
: show_all_breakpoints_command
| delete_breakpoint_command
| enable_breakpoint_command
| disable_breakpoint_command
Each entry in the breakpoint table has the following properties:
- A unique breakpoint number that is used to identify and refer to
that breakpoint.
- An event description that characterizes the circumstances under which
the breakpoint triggers.
- Actions (a possibly empty list of Ladebug commands) to be
performed when the breakpoint triggers.
- A final disposition: either continue or break (stop).
- Enabled and disabled states.
In addition to the main effects of a breakpoint definition, as discussed
in Breakpoint Definitions, a breakpoint
definition also sets the debugger variable
$lasteventmade
to the breakpoint number of the breakpoint just
defined. You can recall this value for later use if desired. For example:
(ladebug) stop in List<Node>::append
[#2: stop in void List<Node>::append(class Node* const) ]
(ladebug) cont
[2] stopped at [void List<Node>::append(class Node* const):148 0x120001dbc]
148 if (!_firstNode)
(ladebug) print $lasteventmade
2
(ladebug) set $my_break = $lasteventmade
(ladebug) print $my_break
2
If an error occurs in a breakpoint command, the variable
$lasteventmade
is not changed.
Use the status
command to display the current breakpoint table:
show_all_breakpoints_command
: status
Each entry in the current breakpoint table is displayed showing all of its
properties. For example:
(ladebug) status
#1 PC==0x120002418 in int main(void) "x_list.cxx":182 { stop }
#2 PC==0x120001dbc in void List<Node>::append(class Node* const) "x_list.cxx":148 { break }
#3 Access memory (write) 0x11fffbf90 to 0x11fffbf97 { stop }
When large or complex values are passed by value to the routine in the status line,
the output can be voluminous. You can set the control variable
$statusargs
to 0
to suppress the output of argument type information in the status line.
When a breakpoint is defined, it is enabled by default. When the debugger starts
or resumes process execution, it first adapts the process so that it can
detect when the given events occur. You can disable a breakpoint
so it is not involved in determining when the process should next stop.
A breakpoint that is no longer required can be deleted entirely:
disable_breakpoint_command
: disable all
| disable breakpoint_number_expression ,...
enable_breakpoint_command
: enable all
| enable breakpoint_number_expression ,...
delete_breakpoint_command
: delete all
| delete breakpoint_number_expression ,...
For example:
(ladebug) disable 1
(ladebug) status
#1 PC==0x120002418 in int main(void) "x_list.cxx":182 { stop } Disabled
#2 PC==0x120001dbc in void List<Node>::append(class Node* const) "x_list.cxx":148 { break }
#3 Access memory (write) 0x11fffbf90 to 0x11fffbf97 { stop }
(ladebug) disable 10 - 8,1 + 1 + 1
(ladebug) status
#1 PC==0x120002418 in int main(void) "x_list.cxx":182 { stop } Disabled
#2 PC==0x120001dbc in void List<Node>::append(class Node* const) "x_list.cxx":148 { break } Disabled
#3 Access memory (write) 0x11fffbf90 to 0x11fffbf97 { stop } Disabled
(ladebug) delete 1
(ladebug) status
#2 PC==0x120001dbc in void List<Node>::append(class Node* const) "x_list.cxx":148 { break } Disabled
#3 Access memory (write) 0x11fffbf90 to 0x11fffbf97 { stop } Disabled
(ladebug) enable all
(ladebug) status
#2 PC==0x120001dbc in void List<Node>::append(class Node* const) "x_list.cxx":148 { break }
#3 Access memory (write) 0x11fffbf90 to 0x11fffbf97 { stop }
Chapter 8Looking at the Code, the
Data, and Other Process Information
This chapter describes how to look at the following components of a running
process:
The debugger supports commands to perform the following operations with source
files:
- Determine the location of the source files
- Select a particular file as the current file
- List portions of the current file
- Search through the current file for target strings
browse_source_command
: source_directory_mapping_command
| source_searchlist_command
| select_source_file_command
| list_source_file_command
| search_source_file_command
Special debugging information that the compiler puts in the .o
files
correlates the machine instructions and data back to the source files and the
positions they came from.
Source files are compiled and linked into executable files. During
debugging, the debugger tries to find these source files to display them
for you. If the source files have moved, or if the paths to them are relative,
the debugger may not be able to locate them. All the information the debugger
needs comes from the executable files or shared libraries, not from the source
files.
8.1.1 How the Debugger Finds Source Files
The debugger searches for a source file
(dir_name/base_name
) using the following algorithm:
- If
dir_name
is mapped to
another source directory (mapped_dir_name
), look for
mapped_dir_name/base_name
.
- If step 1 fails to find a readable file:
Case 1: If dir_name
is absolute, look for dir_name/base_name
.
Case 2: If dir_name
is relative, for each entry use_dir
in
use_list
,
look for use_dir/dir_name/base_name
. The use_list
entries
are tried in the order they appear in the use_list
.
- If step 2 fails, for each entry
use_dir
in use_list
, look for
use_dir/base_name
. Just as in step 2, the use_list
entries are tried in the order they appear in the use_list
.
- If step 3 fails, the debugger cannot find any source file.
The debugger uses the first-found readable file as the source file.
The debugger has source directory mapping commands that do the following:
- Inform you in which directories the debugger is looking for
the source files.
- Allow you to designate directories in which the debugger will
look for the source files.
The following example shows how to use source directory mapping.
Suppose you compile x_solarSystem
as follows:
% pwd
/usr/users/ladebug/sandbox/test/src/common/Examples
% ls -R
bin/ src/
./bin:
x_solarSystem*
./src:
solarSystemSrc/
./src/solarSystemSrc:
base_class_includes/ main/ star.cxx
derived_class_includes/ orbit.cxx
heavenlyBody.cxx planet.cxx
./src/solarSystemSrc/base_class_includes:
heavenlyBody.h orbit.h
./src/solarSystemSrc/derived_class_includes:
planet.h star.h
./src/solarSystemSrc/main:
solarSystem.cxx
% cd src
% cc -g -o ../bin/x_solarSystem \
-IsolarSystemSrc/base_class_includes \
-IsolarSystemSrc/derived_class_includes \
main/solarSystem.cxx heavenlyBody.cxx orbit.cxx planet.cxx star.cxx
Then you move the directory solarSystemSrc
elsewhere:
% mv solarSystemSrc movedSolarSystemSrc
Now debug x_solarSystem
in
/usr/users/ladebug/sandbox/test/src/common/Examples/bin
:
(ladebug) list $curline - 10: 20
Source file not found or not readable, tried...
./solarSystemSrc/main/solarSystem.cxx
../src/solarSystemSrc/main/solarSystem.cxx
/usr/proj/ladebug-builds/build-latest/test/src/common/Examples/bin-alpha-osf1/solarSystemSrc/main/solarSystem.cxx
./solarSystem.cxx
../src/solarSystem.cxx
/usr/proj/ladebug-builds/build-latest/test/src/common/Examples/bin-alpha-osf1/solarSystem.cxx
The debugger cannot find the file because it has been moved to another
directory.
The following command displays a summary of the source directories
in a.out
. The
ellipsis (...) here means that solarSystemSrc
contains one or more source
directories.
(ladebug) show source directory
.
solarSystemSrc
...
/usr/include/cxx
Information: You can further expand a '...' using the command
show source directory <directory>
or
show all source directory <directory>
where <directory> is the directory on the line above the '...'.
The first command displays only the children of <directory>, whereas
the second command displays all the descendants of <directory>.
The following command directs the debugger to look for source files originally
in
solarSystemSrc
in movedSolarSystemSrc
instead. This
time, the debugger finds the source file.
(ladebug) map source directory solarSystemSrc ../src/movedSolarSystemSrc
(ladebug) list $curline - 10: 20
104
105 // Insert the new entry appropriately
106 //
107 if (iAmBiggerThan < biggestCount) {
108 biggestMoons[iAmBiggerThan] = moon;
109 }
110 }
111
112 void main()
113 {
> 114 unsigned int j = 1; // for scoping examples
115 for (unsigned int i = 0; i < biggestCount; i++)
116 biggestMoons[i] = NULL;
117
118 Star *sun = new Star("Sol", G, 2);
119 buildOurSolarSystem(sun);
120 sun->printBodyAndItsSatellites(j);
121 printBiggestMoons();
122 }
The following command gives a complete list of source directories. As you can
see, solarSystemSrc
is mapped to movedSolarSystemSrc
.
As a side effect of mapping
solarSystemSrc
to movedSolarSystemSrc
,
the subdirectories in solarSystemSrc
are
mapped to their counterparts under movedSolarSystemSrc
.
(ladebug) show all source directory
.
solarSystemSrc *=> ../src/movedSolarSystemSrc
solarSystemSrc/base_class_includes => ../src/movedSolarSystemSrc/base_class_includes
solarSystemSrc/derived_class_includes => ../src/movedSolarSystemSrc/derived_class_includes
solarSystemSrc/main => ../src/movedSolarSystemSrc/main
/usr/include/cxx
To summarize, the debugger provides the following four commands for checking
and setting source directory mappings:
source_directory_mapping_command
: show source directory [ directory_name ]
| show all source directory [ directory_name ]
| map source directory from_directory_name to_directory_name
| unmap source directory from_directory_name
Use the show source directory
command to display the directory mapping
information of directory_name
and its child directories (or immediate
subdirectory). If directory_name
is not specified, the mapping
information of all the source directories whose parent is not a source
directory is displayed.
The show all source directory
command is identical to the
show source directory
command except that the
mapping information of all the descendants of directory_name
is displayed:
(ladebug) show source directory
.
solarSystemSrc *=> ../src/movedSolarSystemSrc
...
/usr/include/cxx
(ladebug) show all source directory
.
solarSystemSrc *=> ../src/movedSolarSystemSrc
solarSystemSrc/base_class_includes => ../src/movedSolarSystemSrc/base_class_includes
solarSystemSrc/derived_class_includes => ../src/movedSolarSystemSrc/derived_class_includes
solarSystemSrc/main => ../src/movedSolarSystemSrc/main
/usr/include/cxx
When you further expand ellipsis points (...), where directory
is the
directory on the line above the ellipsis points:
- The
show source directory
command displays only the children
of
directory_name
.
- The
show all source directory
command displays all the
descendants of directory_name
.
Use the map source directory
command to tell the debugger that the source
files in the directory from_directory_name
can now be found in
to_directory_name
.
The unmap source directory
command maps
from_directory_name
back to itself;
in other words, if from_directory_name
has been mapped to some other directory,
this command will restore its default mapping. For example:
(ladebug) show source directory
.
solarSystemSrc *=> ../src/movedSolarSystemSrc
...
/usr/include/cxx
(ladebug) show source directory solarSystemSrc
solarSystemSrc *=> ../src/movedSolarSystemSrc
solarSystemSrc/base_class_includes => ../src/movedSolarSystemSrc/base_class_includes
solarSystemSrc/derived_class_includes => ../src/movedSolarSystemSrc/derived_class_includes
solarSystemSrc/main => ../src/movedSolarSystemSrc/main
(ladebug) unmap source directory solarSystemSrc
(ladebug) show source directory solarSystemSrc
solarSystemSrc
solarSystemSrc/base_class_includes
solarSystemSrc/derived_class_includes
solarSystemSrc/main
NOTE: The symbol *=>
means that you are setting the mapping
explicitly
using the map source directory
command, whereas =>
means
that the mapping is derived from an existing explicit mapping.
By default, the use_list
is (1) the current directory and
(2) the directory containing the executable file. Each process has its own
use_list
. You can also use the ladebug
command
-I
option to specify search directories.
The following commands let you view and modify the use_list
:
source_searchlist_command
: use_command
| unuse_command
Enter the use
command without an argument to list the directories
in which the
debugger searches for source code files. Specify a directory argument to make
source code files in that directory available to the debugger. You can also use
the ladebug
command -I
option to specify search directories,
which puts those directories in the use_list
.
You can customize your debugger environment source code search paths by adding
commands to your .dbxinit
file that use the
use
command:
use_command
: use [directory_name ...]
If the directory_name
is specified,
it is
either appended to or replaces the use_list
, depending on whether the
value of the $dbxuse
debugger variable
is zero (append) or nonzero (replace).
The unuse
command removes entries from the use_list
:
unuse_command
: unuse [directory_name ...]
| unuse *
Enter the unuse
command without the
directory_name
to set the search list to the default (the home directory, the current
directory, and the directory containing the executable file). Include the
directory names to remove them from the search list. The asterisk
(*) argument removes all directories from the search list.
The debugger has a concept of current source file, so you do not have
to explicitly specify a source file in many commands.
Whenever the process stops, the current source file is set to the
source file for the code currently executing.
The commands up
, down
, class
,
and file
also set the current source file.
You can see and modify the current source file selection:
select_source_file_command
: file [ filename ]
: fileexpr [ expression ]
Use the file
command without a file name to display the name
of the current file scope. Include the file name to change the file scope.
Change the file scope to set a breakpoint in a function not in the file
currently being executed.
To see source code for or set a breakpoint in a function not in the file
currently being executed,
use the file
command to set the file scope.
If the file name is not a literal, use the fileexpr
command. For example, if you have a script that calculates a file name in a debugger
variable or in a routine that returns a file name as a string, you can use
fileexpr
to set the file.
The following example uses the file
command to set
the debugger file scope to a file different from the main program, and
then stops at line number 26 in that file. This example also shows the
fileexpr
command setting the current scope back to
the original file, which is solarSystem.cxx
.
(ladebug) run
[1] stopped at [void main(void):114 0x120004040]
114 unsigned int j = 1; // for scoping examples
(ladebug) file
solarSystemSrc/main/solarSystem.cxx
(ladebug) set $originalFile = "solarSystem.cxx"
(ladebug) list 24: 10
24 Moon *phobos = new Moon("Phobos", 9, 11, mars);
25 Moon *deimos = new Moon("Deimos", 23, 6, mars);
26
27 Planet *jupiter = new Planet("Jupiter", 778330, sun);
28 Moon *io = new Moon("Io", 422, 1815, jupiter);
29 Moon *europa = new Moon("Europa", 671, 1569, jupiter);
30 Moon *ganymede = new Moon("Ganymede", 1070, 2631, jupiter);
31 Moon *callisto = new Moon("Callisto", 1883, 2400, jupiter);
32 Moon *amalthea = new Moon("Amalthea", 181, 98, jupiter);
33
(ladebug) file star.cxx
(ladebug) list 24: 10
24 // Stars are simple objects
25 //
26 Star::Star(
27 char* name,
28 StellarClass classification,
29 StellarSubclass subclassification)
30 : HeavenlyBody(name),
31 _classification(classification),
32 _subclassification(subclassification)
33 {
(ladebug) stop at 26
[#2: stop at "solarSystemSrc/star.cxx":26 ]
(ladebug) cont
[2] stopped at [Star::Star(char*, enum StellarClass, StellarSubclass):26 0x120004b4c]
26 Star::Star(
(ladebug) file
solarSystemSrc/star.cxx
(ladebug) fileexpr $originalFile
(ladebug) file
solarSystemSrc/main/solarSystem.cxx
(ladebug) list 24: 10
24 Moon *phobos = new Moon("Phobos", 9, 11, mars);
25 Moon *deimos = new Moon("Deimos", 23, 6, mars);
26
27 Planet *jupiter = new Planet("Jupiter", 778330, sun);
28 Moon *io = new Moon("Io", 422, 1815, jupiter);
29 Moon *europa = new Moon("Europa", 671, 1569, jupiter);
30 Moon *ganymede = new Moon("Ganymede", 1070, 2631, jupiter);
31 Moon *callisto = new Moon("Callisto", 1883, 2400, jupiter);
32 Moon *amalthea = new Moon("Amalthea", 181, 98, jupiter);
33
The simplest way to see a source file is to use a text editor.
The edit
command will display an
editor on the current file, using
the current definition of the EDITOR
environment variable, if one exists.
However, some primitive inspection capabilities are built into the
debugger. The list
command displays source lines, beginning with the
source code line corresponding to one of the following:
- The position of the program counter
- The last line listed, if multiple list commands are entered
- The line number specified as the first argument to the
list
command
list_source_file_command
: list [ line_expression ]
| list line_expression , line_expression
| list line_expression : line_expression
line_expression
: expression
If specified, the first expression must evaluate to either
an integer (the line number of the first line to display within the current source
file) or a function (the first line of the function).
Specify the exact range of source lines as either
a comma followed by the expression for the
last line,
or a colon followed by the expression for the
the number of lines. This second expression must evaluate to
an integer value.
If a second expression is not given, the
debugger shows 20 lines, fewer if the end of
source file is reached.
For example, to list lines 16 through 20:
(ladebug) list 16, 20
16
17 class Node {
18 public:
19 Node ();
20
For example, to list 6 lines, beginning with line 16:
(ladebug) list 16: 6
16
17 class Node {
18 public:
19 Node ();
20
21 virtual void printNodeData() const = 0;
The following search commands search through the current source file
to help you find the lines to list:
search_source_file_command
: / [ string ]
| ? [ string ]
NOTE: The string is actually just the
rest of the line, not a string literal.
The rest of the line is still having alias expansion performed on it.
Use a slash (/
) to search forward from the most recently
listed
line; use a question mark (?
) to search backward. Like most searches, it will stop at
the end (or beginning) of the file being searched, and will wrap if the command
is repeated at that point.
When the string is omitted, the previous search continues
from where it found the string. When the string is present, the search starts from either
the
start (/
) or the end (?
) of the
current line.
When a match is found, the debugger lists the line number and the line. That line
becomes the starting point for any further searches, or for a
list
command. For example:
- To locate
_firstNode
:
(ladebug) /_firstNode
69 NODETYPE* _firstNode;
- Then to locate
append
before line 69:
(ladebug) ?append
65 void append (NODETYPE* const node);
- Then to locate
append
after line 65:
(ladebug) /append
145 void List<NODETYPE>::append(NODETYPE* const node)
A thread is a single, sequential flow of control within a process. Each thread
contains a single point of execution. Threads execute within (and share) a single
address space; therefore, a process's threads can read and write the same
memory locations. For further information, see the
Tru64 UNIX Guide to the POSIX Threads Library.
The debugger supports two levels of threads:
pthreads
(user application threads), also known as POSIX threads
- Kernel threads (operating-system-level threads), also known as native threads
To specify the thread level, set the
$threadlevel
debugger variable to one of the following strings:
decthreads
for POSIX Threads Library debugging
native
for kernel thread debugging
For example:
(ladebug) set $threadlevel = "decthreads"
For core file debugging, the $threadlevel
is always set to
"native"
.
You can use a variety of commands to manipulate the threads:
thread_command
: show_thread_command
| switch_thread_command
| show_condition_variable_command
| show_mutex_variable_command
| pthread_command
You can use the following commands to display threads:
show_thread_command
: show thread [ thread_id_list ] [ thread-state-filter ]
thread_id_list
: thread_id ,...
| *
thread_id
: expression
thread_state_filter
: with state eq thread_state
eq
: ==
(for Ada, C, and C++)
| .eq.
(for Fortran)
| =
(for Cobol)
| equal [ to ]
(for Cobol)
thread_state
: ready
| running
| terminated
| blocked
Use the show thread
command without parameters to list all the
threads known to the debugger.
If you specify one or more thread identifiers, the debugger displays
information about the threads you specify, if the thread matches what you
specified in the list. If you omit a thread specification, the debugger
displays information for all threads.
Use the show thread
commands to list threads that have specific
characteristics, such as threads that are currently blocked.
For example:
(ladebug) print $threadlevel
"decthreads"
(ladebug) show thread
Thread Name State Substate Policy Pri
------ ------------------------- --------------- ----------- ------------ ---
* 1 default thread running VP 3 SCHED_OTHER 19
-1 manager thread blk SCS SCHED_RR 19
-2 null thread for slot 0 running VP 1 null thread -1
-3 null thread for slot 1 ready VP 3 null thread -1
-4 null thread for slot 2 new new null thread -1
-5 null thread for slot 3 new new null thread -1
> 2 threads(0x140000798) blocked cond 3 SCHED_OTHER 19
3 threads+8(0x1400007a0) blocked cond 3 SCHED_OTHER 19
4 threads+16(0x1400007a8) blocked cond 3 SCHED_OTHER 19
5 threads+24(0x1400007b0) blocked cond 3 SCHED_OTHER 19
6 threads+32(0x1400007b8) blocked cond 3 SCHED_OTHER 19
(ladebug) set $threadlevel = "native"
(ladebug) print $threadlevel
"native"
(ladebug) show thread
Id State
* 0x9 stopped
* 0x9 unstarted
0x3 unstarted
0x7 unstarted
NOTE: In the output, the right bracket indicator (>) marks the
current thread, whereas the asterisk (*) indicator marks the thread with
the event that stopped the application.
You can switch to a different thread as the current thread. The debugger
variable $curthread
contains the thread identifier
of the current thread:
switch_thread_command
: thread [ thread_id ]
The $curthread
value is updated when program
execution stops or completes.
You can modify the current thread by assigning $curthread
a valid
thread identifier. This is equivalent to issuing the thread
thread_id
command. When there is no process or program,
$curthread
is set to 0.
Use the thread
command without a thread identifier to
identify the current thread. Supply a thread identifier to make another
thread the current thread.
A mutex (mutual exclusion) semaphore is a programming flag that allows multiple pthread
s
to synchronize access to shared resources, to ensure the following:
- All threads see a clean and consistent view of the data, without allowing one
thread to change something while another thread is reading it.
- Two threads do not change different parts of the data at the same time,
possibly in inconsistent ways.
Use the show mutex
command to list information about currently available
pthread
mutexes:
show_mutex_variable_command
: show mutex [ mutex_id_list ] [ mutex_state_filter ]
mutex_id_list
: mutex_id ,...
| (mutex_id ,...)
mutex_state_filter
: with state eq mutex_state
eq
: ==
(for Ada, C, and C++)
| .eq.
(for Fortran)
| =
(for Cobol)
| equal [ to ]
(for Cobol)
mutex_state
: locked
If you specify one or more mutex identifiers, the
debugger displays information about only those mutexes specified, provided that
the list matches the identifiers of currently available mutexes. If you omit the
mutex identifier specification, the debugger displays information about all
mutexes currently available.
Use the show mutex with state == locked
command to display
information exclusively for locked mutexes.
If $verbose
is set to 1, the sequence numbers of the
threads locking the mutexes are displayed.
The following example shows the output from a simple show mutex
command:
(ladebug) show mutex
Mutex Name State Owner Pri Type Waiters (+Count)
------ ------------------------- ----- ------ --- -------- --------------------
1 malloc heap Normal
2 malloc hash Normal
3 malloc cache[0] Normal
4 malloc cache[1] Normal
5 malloc cache[2] Normal
6 malloc cache[3] Normal
7 malloc cache[4] Normal
8 malloc cache[5] Normal
9 malloc cache[6] Normal
10 malloc cache[7] Normal
11 malloc cache[8] Normal
12 malloc cache[9] Normal
13 malloc cache[10] Normal
14 malloc cache[11] Normal
15 malloc cache[12] Normal
16 malloc cache[13] Normal
17 malloc cache[14] Normal
18 malloc cache[15] Normal
19 malloc cache[16] Normal
20 malloc cache[17] Normal
21 malloc cache[18] Normal
22 malloc cache[19] Normal
23 malloc cache[20] Normal
24 malloc cache[21] Normal
25 malloc cache[22] Normal
26 malloc cache[23] Normal
27 malloc cache[24] Normal
28 malloc cache[25] Normal
29 malloc cache[26] Normal
30 malloc cache[27] Normal
31 malloc cache[28] Normal
32 brk Normal
33 exc cr Recurs
34 exc read rwl Normal
35 known mutex queue Normal
36 known cond queue Normal
37 known VP queue Normal
38 known rwl queue Normal
39 VM 0 lookaside Normal
40 VM 1 lookaside Normal
41 VM 2 lookaside Normal
42 VM 0 cache Normal
43 VM 1 cache Normal
44 VM 2 cache Normal
45 debugger client registry Normal
46 Global lock Recurs
47 ldr Recurs
48 prime_list(0x140000660) Normal
49 cond_mutex(0x1400006c0) Normal
50 current_mutex(0x140000690 Normal
51 curr_worker_mutex(0x14000 Lock Normal
If the application being debugged has no pthread
s, or if the
$threadlevel
is set
to native
, an appropriate message is issued.
A condition variable is a pthread
synchronization object used in conjunction
with a mutex. A condition variable is used when a thread has locked a mutex to gain
access to data and then finds it must wait for some other thread to change some
aspect of the data before it can continue:
show_condition_variable_command
: show condition [ condition_id_list ] [ condition_state_filter ]
condition_id_list
: condition_id ,...
| (condition_id ,...)
condition_id
: integer_constant
condition_state_filter
: with state eq condition_state
condition_state
: wait
Use the show condition
command to list information about currently
available condition variables. If you supply one or more condition
identifiers, the debugger displays information about the condition variables
you specify, provided that the list matches the identities of currently available condition variables. If you omit
the condition variable specification, the debugger displays information about
all the condition variables currently available.
Use the show condition with state == wait
command to display
information only for condition variables that have one or more threads waiting.
If $verbose
is set to 1, the sequence numbers of the threads waiting
on the condition are displayed.
The following example shows output from a simple
show condition
command:
(ladebug) show condition
Cond Name Mutex Type Waiters (+Count)
------ ------------------------- ------ ----- ---------------------------------
1 _exc_read_mutex+72(0x3ffc
2 _exc_read_mutex+112(0x3ff
3 cond_var(0x140000720) 49 2, 3, 4, 5, 6
4 curr_worker(0x140000748)
If the application being debugged has no pthreads
, or if the
$threadlevel
is set to
native
, an appropriate message is issued.
You can use the
where
command to display the
stack trace of current threads. You can specify one or more threads or all threads.
The print
command evaluates an optional
expression in the context of the current thread and displays the result.
The call
command evalutes an expression in the context
of the current thread and makes the call in the context of the current thread.
The printregs
command prints the registers for the
current thread.
You can pass an undocumented string directly into the undocumented
pthread
debugging support. This is an internal debugging aid, not intended for
general use:
pthread_command
: pthread string
Most programming languages have some concept of functions, routines, or
subroutines, capturing the notion of code that is invoked from many places.
A running program needs a call stack of call frames for the called functions.
Each call frame contains both the information needed to return to its caller
and the information needed to provide the local variables of the function.
The machine code generated for these functions maintains this call stack.
Some of this maintenance is done before the call, some at the start of the
called function, some at the end of the called function, and some after the
call.
Non-optimized machine code is usually very easy to correlate with the source
code, but optimized machine code can be tricky. See
Call Frames and Optimized
Code and Call Frames and Machine Code Correlation
for more information.
The debugger controls the call stacks of all the threads; you can use it to
examine and manipulate call stacks, and use them as a basis for further queries:
call_stack_command
: show_stack_command
| change_stack_frame_command
| pop_stack_frame_command
When your process is stopped by the debugger, you can show the call stack
of the thread that caused the stoppage, or the call stack of any other thread.
The following commands show the most recent call frames on the call stack of the current
or specified threads:
show_stack_command
: where [ expression ] [ thread_specifier ]
thread_specifier
: thread thread_id ,...
| thread all
thread_id
: expression
If specified, the expression must evaluate
to a nonnegative integer. You can specify the number of call frames to show.
If not specified, all the call frames for the thread are shown.
If specified, the
thread_specifier
specifies the
threads whose call stacks are to be shown. If not specified, just the
current thread is used.
When large and complex values are passed by value to a routine on the
stack, the output of the where
command can be
voluminous. You can set the control variable
$stackargs
to 0
to suppress the output of argument values in the where
command.
The stack trace provides the following information for each call level:
Call level |
The number used to refer to a call level on the stack. The function
entered most recently is at level 0. Its caller is at level 1.
|
Memory address |
The address of the next instruction to be executed at this level. |
Function name |
The name of the function for the memory address. |
File name |
The source file for the memory address. |
Line number |
The number of the next source line of the memory address. |
If your call stack seems to be missing routines, you may
be seeing the result of a compiler optimization known as
"tail calls."
If your call stack is corrupted, you may see random numbers without any
routine names. In this case, it is likely that your application has gotten
lost. Typically, this type of call stack display means that your application
has lost track of the real stack and real code location, and is now executing
random bits of memory, interpreting them as instructions.
If you are coding in C++, one of the most common ways to get a corrupt stack
is for your code to try to execute a method on an invalid object. If the
object has already been deleted, has not yet been initialized, is not there,
or is of a completely different type, then the virtual function table will not
be correct, and the application will be treating random memory as the virtual
function table and calling a random place. In this case, you may find the
history tool useful to locate the problem. See the
Ladebug
Web page FAQ for more information about the history tool.
You can select one of the call frames as the starting point for examining
variables. This call frame provides the current scope in the program
for which variables exist, and tells the debugger which instance of those
variables whose values you want to see:
change_stack_frame_command
: up [ expression ]
| down [ expression ]
| func [ loc ]
Use the up
command or the down
command without
the expression to change to the call frame
located one level up or down the stack.
Specify an expression that evaluates to an integer to change the
call frame up or down the specified number of levels. If
the number of levels exceeds the number of active calls on the stack in the
specified direction, the
debugger issues a warning message and the call frame does not change.
When the current call frame changes, the debugger displays the source line
corresponding to the last instruction executed in the function executing the
selected call frame.
When large and complex values are passed by value to a routine on the
stack, the output of the up
and
down
commands can be
voluminous. You can set the control variable
$stackargs
to 0
to suppress the output of argument values in the up
and
down
commands.
Use the func
command without the loc
to display the
current function.
To change the function scope to a function that has a call frame in the
call stack, specify the loc
either as the name of the function or as an integer
expression evaluating to
the call level. If you specify the name, the most-recently entered call
frame for that function becomes the current call frame.
If no frames are available to select from, the debugger context is set to the
static context of the named function. The current scope and current language
are set based on that function. Types and static variables local to that
function are now visible and can be evaluated.
If you enter an integer expression, the debugger moves to the frame at
level n
, just as if you had entered up n
at the level 0 function.
In the following example, the current call frame is changed
to one for method Planet::print
so that a variable in that instance
of print()
can be displayed:
(ladebug) where 4
#0 0x1200047dc in ((Planet*)0x140004060)->Planet::print(i=2) "solarSystemSrc/planet.cxx":19
#1 0x12000422c in ((HeavenlyBody*)0x140004060)->HeavenlyBody::printBodyAndItsSatellites(i=2) "solarSystemSrc/heavenlyBody.cxx":62
>2 0x120004254 in ((HeavenlyBody*)0x140002c00)->HeavenlyBody::printBodyAndItsSatellites(i=1) "solarSystemSrc/heavenlyBody.cxx":68
#3 0x120004110 in main() "solarSystemSrc/main/solarSystem.cxx":120
(ladebug) list $curline - 5: 10
62 print(i);
63
64 // Recursively deal with the satellites. Redeclare i for scoping examples.
65 //
66 unsigned int j = 1;
67 for (HeavenlyBody* i = _firstSatellite; i; i = i->_outerNeighbor) {
> 68 i->printBodyAndItsSatellites(j++);
69 }
70 }
(ladebug) whatis i
class HeavenlyBody* i
(ladebug) print i
0x140004060
(ladebug) func Planet::print
virtual void Planet::print(unsigned int) in solarSystemSrc/planet.cxx line No. 19:
19 cout << "(" << i
(ladebug) where 4
>0 0x1200047dc in ((Planet*)0x140004060)->Planet::print(i=2) "solarSystemSrc/planet.cxx":19
#1 0x12000422c in ((HeavenlyBody*)0x140004060)->HeavenlyBody::printBodyAndItsSatellites(i=2) "solarSystemSrc/heavenlyBody.cxx":62
#2 0x120004254 in ((HeavenlyBody*)0x140002c00)->HeavenlyBody::printBodyAndItsSatellites(i=1) "solarSystemSrc/heavenlyBody.cxx":68
#3 0x120004110 in main() "solarSystemSrc/main/solarSystem.cxx":120
(ladebug) list $curline - 5: 10
14 {
15 }
16
17 void Planet::print(unsigned int i) const
18 {
> 19 cout << "(" << i
20 << ") Planet [" << HeavenlyBody::name() << "]; ";
21 printOrbitalParameters();
22 cout << endl;
23 }
(ladebug) whatis i
unsigned int i
(ladebug) print i
2
In the previous example, instead of entering
func Planet::print
, you can enter
down 2
. (You would use down
in this case
because the current call frame at the start of the example was not the bottommost
frame.) The final stack trace in this example lists
a call frame for function Planet::print
as the current call frame
(denoted by the > character).
8.3.2 The pop
Command
The pop
command removes one or more call frames from the call
stack:
pop_stack_frame_command
: pop [ expression ]
The default is one call frame. The pop
command undoes the work
already done by the removed execution frames. It does not, however, reverse
side effects, such as changes to global variables.
NOTE: Because it is extremely
unlikely this will fix all the effects of a half-executed call, this command
is not recommended for general use. Furthermore, the pop
command does
not provide a way to specify a return value when the frame being discarded corresponds
to a function that should return a value. You may need to use the
assign
command to restore the values of global variables.
Instead of the pop
command, you may want to use the
return
command, which finishes the
call corresponding to the selected frame.
8.3.3 Call Frames and Optimized Code
When optimized machine code is generated by the compilers, the compiler
generates code that maintains the call stack,
but sometimes the function boundaries are changed in one of two ways:
- Inlining is when the compiler completely eliminates the call
by instead generating the instructions for the called function at the call site,
usually followed by merging those instructions with the other instructions
surrounding the call site.
- Outlining is when the compiler creates a function where one did not
exist explicitly in the source. For example, the compiler turns a loop body
into a function, so that it can generate code that uses threads to execute the
different iterations in parallel; or the compiler creates a single shared function
to replace several sections of the source that are similar.
Depending on the information the compiler makes available to the debugger,
inlined calls may or may not show up in the call stack display.
Outlined calls do show up, and are correlated to the code they came from.
The compiler will probably have supplied the debugger with some invented
name for the function.
8.3.4 Call Frames and Machine Code Correlation
On a RISC processor, such as a Compaq Alpha processor, the following is the
machine code typically generated for a call to a function:
- The machine code before the call
performs the following operations:
- Sets some context registers
- Puts the parameters either in registers or in memory
- Loads the address of the function into a register
- Loads the address to return to into a register
- Branches to the function
- The machine code at the start of the called function
performs the following operations:
- Sets some context registers
- Allocates stack space
- Saves some registers in the stack space
- Performs some setup of the local variables
- The machine code at the end of the called function
performs the following operations:
- Restores the saved registers from the stack space
- Deallocates the stack space
- Branches to the address to return to
- The machine code at the return address of the call
frame sets some context registers.
When the thread is partway through the call frame creation or tear-down, the
debugger will still show the call frame, but will not be able to show correct
values for the variables or parameters.
For nonstatic member functions, the implicit this pointer is
displayed as the address on the stack trace along with the class type of
the object, as shown in the following example:
(ladebug) stop in List<Node>::print
[#3: stop in void List<Node>::print(void) ]
(ladebug) cont
[3] stopped at [void List<Node>::print(void):162 0x120001e90]
162 Node* currentNode = _firstNode;
(ladebug) where 2
>0 0x120001e90 in ((List<Node>*)0x11fffbf90)->List<Node>::print() "x_list.cxx":162
#1 0x1200027b4 in main() "x_list.cxx":203
8.4 Looking at the Data
After you have seen the call stack
(show_stack_command
), selected the call
frame containing the variables you wish to examine
(change_stack_frame_command
),
and looked at the source this function is executing
(looking at the source
)
, you usually want to examine some of the
variables or even evaluate some expressions.
You can use the print
command and the
call
command to do this. You can also
use the following commands to help you determine what to look at and what you are
seeing:
look_around_command
: various_print_command
| c++_look_around_command
| call_command
| whatis_command
| whereis_command
| which_command
various_print_command
: print_command
| printf_command
| printi_command
| print_registers_command
| printt_command
| dump_command
You can print the values of one or more expressions or all local variables. You can also use the
print
command to evaluate complex expressions involving typecasts, pointer
dereferences, multiple variables, constants, and any legal operators allowed by
the language of the program you are debugging:
print_command
: print [ expression ,... ]
| print rescoped_expression
| print printable-type
| printb [ expression ,... ]
| printd [ expression ,... ]
| printo [ expression ,... ]
| printx [ expression ,... ]
rescoped_expression
: filename ` qual_symbol
| ` qual_symbol
qual_symbol
: expression
| qual_symbol ` expression
For an array, the debugger prints every cell in the array if you do not specify
a specific cell. For arrays or lists, you can use debugger variables and aliases to
do a traversal of the data structure (see the array navigation
example).
Use the $hexints
, $decints
, or
$octints
variables to
select a radix for the output of the print
command. If you do not
want to change the radix permanently, use the
printx
, printd
,
printo
, and printb
commands to print expressions in hexadecimal, decimal, octal, or binary base format, respectively.
Consider the following declarations in a C++ program:
(ladebug) list 59: 2
59 const unsigned int biggestCount = 10;
60 static Moon *biggestMoons[biggestCount];
The following example uses the print
command to display a
nonstring array:
(ladebug) print biggestMoons
[0] = 0x1400043c0,[1] = 0x140004720,[2] = 0x140004420,[3] = 0x140004300,[4] = 0x140004120,[5] = 0x140004360,[6] = 0x140004ae0,[7] = 0x1400049c0,[8] = 0x1400046c0,[9] = 0x140004a20
The following example shows how to print individual values of an array:
(ladebug) print biggestMoons[3]
0x140004300
(ladebug) print *biggestMoons[3]
class Moon {
_radius = 1815;
_name = 0x1200020b0="Io"; // class Planet::HeavenlyBody
_innerNeighbor = 0x0; // class Planet::HeavenlyBody
_outerNeighbor = 0x140004360; // class Planet::HeavenlyBody
_firstSatellite = 0x0; // class Planet::HeavenlyBody
_lastSatellite = 0x0; // class Planet::HeavenlyBody
_primary = 0x1400042a0; // class Planet::Orbit
_distance = 422; // class Planet::Orbit
_name = 0x140005a80="Jupiter 1"; // class Planet::Orbit
}
8.4.1.1 Dereferencing Pointers
Pointers are variables that contain addresses. By dereferencing
a pointer in the command interface, you can print the value at the address
pointed to by
the pointer. In C and C++ programs, variables containing a pointer are dereferenced
using the * operator. The following example shows how to dereference a pointer
in C++ programs:
(ladebug) whatis newNode
class IntNode* newNode
(ladebug) print newNode
0x140002c00
(ladebug) print *newNode
class IntNode {
_data = 1;
_nextNode = 0x0; // class Node
}
8.4.1.2 Printing C Strings
The debugger does not print more than the first
$maxstrlen
characters
of a null-terminated string. Change this debugger variable if it is
showing either more or less than you wish to see.
8.4.1.3 Printing Floating Point Numbers
Floating point numbers are represented inside the computer in binary floating point.
They are converted to decimal floating point when printed. The two formats are not
the same, and some numbers are easily represented in decimal but not in binary
(for example the number 1.1). The internal binary form for such numbers is an approximation,
the closest that can be made given the number of bits available.
Normally, when a binary floating point number is printed, the shortest decimal number
that would be represented by that binary number is used as the number to print, as it
is a legitimate representation of the internal binary number.
However, to see a more exact (extended form) representation of a binary floating point number,
you can set the $floatshrinking
debugger variable to 0 (zero).
The following example shows the result of converting 1.1 (shortened form) to the
closest long double binary floating point number (extended form):
(ladebug) p $floatshrinking
1
(ladebug) p 1.1
1.1
(ladebug) set $floatshrinking = 0
(ladebug) p 1.1
1.10000000000000000000000000000000008
Currently, the extended forms are only available for long double variables and expressions.
For more detail on floating point representation, see ANSI IEEE Standard 754-1985.
8.4.1.4 Restrictions on the print Command
Expressions containing labels are not supported. Variables involving static
anonymous unions and enumerated types may not be able to be printed. Printing
a structure that is declared but not defined in a compilation unit may generate
an error message indicating that the structure is opaque.
Use the printf
command to format and display a complex
structure. The first argument is a string expression of characters and
conversion specifications using the same format specifiers as the
printf
C function. The printf
command requires
a running target program because it uses libc
.
printf_command
: printf [ format_string [ , expression ,... ] ]
For example:
(ladebug) printf "The PC is 0x%x", $pc
The PC is 0x200027b8
The printi
command takes one or more numerical expressions and interprets each one as
an assembly instruction, printing out the instruction, and its arguments when applicable. This command
is typically used by engineers performing machine-level debugging.
printi_command
: printi [ expression ,... ]
For example:
(ladebug) $curpc/1i
Node::Node(void): x_list.cxx
*[line 77, 0x12000226c] ldq r2, -32712(gp)
(ladebug) $curpc/1dd
0x12000226c: -1537376200
(ladebug) printi -1537376200
ldq r2, -32712(gp)
8.4.4 The printregs Command
Use the printregs
command to
display
the values of all the hardware registers. The list of registers displayed by the debugger is
machine-dependent. By default, most values are displayed in decimal radix. To display the
register values in hexadecimal radix, set the $hexints
variable
to 1.
print_registers_command
: printregs
For example:
(ladebug) printregs
$r0 [$v0] = 1 $r1 [$t0] = 5
$r2 [$t1] = 4 $r3 [$t2] = 3
$r4 [$t3] = 2 $r5 [$t4] = 5
$r6 [$t5] = 0 $r7 [$t6] = 4
$r8 [$t7] = 2 $r9 [$s0] = 3
$r10 [$s1] = 0 $r11 [$s2] = 4
$r12 [$s3] = 351841472 $r13 [$s4] = 4
$r14 [$s5] = 340620416 $r15 [$s6] = 1
$r16 [$a0] = 5368717312 $r17 [$a1] = 2
$r18 [$a2] = 2 $r19 [$a3] = 5
$r20 [$a4] = 5368717360 $r21 [$a5] = 4
$r22 [$t8] = 1 $r23 [$t9] = 5
$r24 [$t10] = 4396973371008 $r25 [$t11] = 1
$r26 [$ra] = 4831847228 $r27 [$t12] = 0
$r28 [$at] = 4831845184 $r29 [$gp] = 5
$r30 [$sp] = 4831834640 $r31 [$zero]= 0
$f0 = 12.34500026702881 $f1 = 0
$f2 = 0 $f3 = 0
$f4 = 0 $f5 = 0
$f6 = 0.10000000000000001 $f7 = 0.02
$f8 = 3.1415926000000001 $f9 = 2.8180000000000001
$f10 = 0.001 $f11 = 2.0000000000000002e-05
$f12 = 2.9999999999999997e-08 $f13 = 0
$f14 = 0 $f15 = 0
$f16 = 0 $f17 = 0
$f18 = 0 $f19 = 0
$f20 = 0 $f21 = 3000000000000000
$f22 = 0 $f23 = 1.5189999999999999
$f24 = 12.345678899999999 $f25 = 0
$f26 = 3.0000000000000001e-100 $f27 = INF
$f28 = 1.4139999999999999 $f29 = 3
$f30 = 1020304050 $f31 = 0
$pc = 0x1200027b8 $ps = 0x8
$fpcr = 0x0 $uniq = 0x0
$vfp = 0x11ffff2c0
The printt
command takes one or more numerical expressions and interprets
each one as the number of seconds since the Epoch (00:00:00 UTC 1 Jan 1970; see ctime
(3)
for more information).
printt_command
: printt [ expression ,... ]
For example:
(ladebug) printt 0
(UTC) Thu Jan 1 00:00:00 1970
(ladebug) printt 978325200
(UTC) Mon Jan 1 05:00:00 2001
Use the dump
command without an argument to list the parameters and
local variables in the current function. To list the parameters and local
variables in an active function, specify it as an argument.
Use the dump .
command (include the dot) to list the parameters and
local variables for all functions active on the stack:
dump_command
: dump qual_symbol
| dump .
For example:
(ladebug) dump
>0 0x1200027b8 in main() "x_list.cxx":203
cNode=0x140004000
cNode1=0x140004030
cNode2=0x140004060
newNode=0x140002c00
newNode2=0x140002c40
nodeList=class List<Node> { ... }
When large and complex values are passed by value to a routine on the
stack, the output of the dump
command can be
voluminous. You can set the control variable
$stackargs
to 0 (zero)
to suppress the output of argument values in the dump
command.
After a breakpoint or a signal suspends program execution, you can execute a
single function in your program by using the call
command, or by
including a function call in the expression argument of a debugger command.
Calling a function lets you test the function's operation with a specific set
of parameters.
call_command
: call call-expression
Specify the function as if you were calling it from within the program. If the
function has no parameters, specify empty parentheses (()
).
For multithreaded applications, the call is made in the context of the current
thread. For C++ applications, when you set the
$overloadmenu
debugger variable to 1 and call an
overloaded function, the debugger lists the overloaded functions and calls the
function you specify.
When the function you call completes normally, the debugger restores the stack
and the current context that existed before the function was called.
While the program counter is saved and restored, calling a function does not
shield the program state from alteration if the function you call allocates
memory or alters global variables. If the function affects global program
variables, for instance, those variables will be changed permanently.
Functions compiled without the debugger option to include debugging
information may lack important parameter information and are less likely to
yield consistent results when called.
The call
command executes the specified function with the parameters
you supply and then returns control to you (at the Ladebug prompt) when the
function returns. The call
command discards the return value of the
function. If you embed the function call in the expression argument of a
print
command, the debugger prints the return value after the
function returns. The following example shows both methods of calling a function:
(ladebug) call earth->distance()
(ladebug) print earth->distance()
149600
In the previous example, the call
command results in the return value being
discarded while the embedded call passes the return value of the function to
the print
command, which in turn prints the value. You can also
embed the call within a more involved expression, as shown in the following
example:
(ladebug) print earth->distance() - 100000
49600
(ladebug) print mars->distance() - earth->distance()
78340
(ladebug) call io->print(3)
(3) Moon [Io], radius [1815] km; <Jupiter 1> orbits at 422 Megameters
All breakpoints or tracepoints defined and enabled during the session are
active when a called function is executing. When program execution halts during function
execution, you can examine program information, execute one line or
instruction, continue execution of the function, or call another function.
When you call a function when execution is suspended in a called function,
you are nesting function calls, as shown in the following example:
(ladebug) where 2
>0 0x120003cf0 in buildOurSolarSystem(sun=0x140004c00) "solarSystemSrc/main/solarSystem.cxx":55
#1 0x120004100 in main() "solarSystemSrc/main/solarSystem.cxx":119
(ladebug) stop in Planet::print
[#2: stop in virtual void Planet::print(unsigned int) ]
(ladebug) call mars->print(1)
[2] stopped at [virtual void Planet::print(unsigned int):19 0x1200047dc]
19 cout << "(" << i
(ladebug) where
>0 0x1200047dc in ((Planet*)0x140006180)->Planet::print(i=1) "solarSystemSrc/planet.cxx":19
#1 0x120002a8c in __start(0x140006180, 0x1, 0x0, 0x0, 0x0, 0x11fffbb90) in /usr/examples/x_solarSystem
(ladebug) next
stopped at [virtual void Planet::print(unsigned int):21 0x120004874]
21 printOrbitalParameters();
(ladebug) stop in Orbit::distance
[#3: stop in Megameters Orbit::distance(void) ]
(ladebug) print distance()
[3] stopped at [Megameters Orbit::distance(void):41 0x1200044b4]
41 return _distance;
(ladebug) where
>0 0x1200044b4 in ((Orbit*)0x1400061b0)->Orbit::distance() "solarSystemSrc/orbit.cxx":41
#1 0x120002a8c in __start(0x1400061b0, 0x140008212, 0xffffffffffffffff, 0x11fffbc22, 0x0, 0x3ffc00d03f0) in /usr/examples/x_solarSystem
(ladebug) disable 3
(ladebug) cont
Called Procedure Returned
stopped at [virtual void Planet::print(unsigned int):21 0x120004874]
21 printOrbitalParameters();
(ladebug) where
>0 0x120004874 in ((Planet*)0x140006180)->Planet::print(i=1) "solarSystemSrc/planet.cxx":21
#1 0x120002a8c in __start(0x3ff8029f9c0, 0x140008212, 0xffffffffffffffff, 0x11fffbc22, 0x0, 0x3ffc00d03f0) in /usr/examples/x_solarSystem
(ladebug) cont
(1) Planet [Mars]; <Sol 4> orbits at 227940 Megameters
Called Procedure Returned
stopped at [void buildOurSolarSystem(class Star*):55 0x120003cf0]
55 Planet *pluto = new Planet("Pluto", 5913520, sun);
Restrictions on the call Command
The debugger supports function calls and expression evaluations that
call functions, with the following limitations:
- The debugger does not support passing and returning structures by value.
- The debugger does not implicitly construct temporary objects for call
parameters.
- Optimization can prevent the debugger from knowing the type of a function
return. Therefore, the debugger assumes returns are of the type
int
if the functions are optimized. If the returns are a different type, it may
be necessary to cast the result when calling the optimized functions.
You can print information about the basic nature of a
whatis_expression
.
The expression can be a normal language expression or the name of a type, function,
or other language entity. The debugger shows you information about the
entity rather than evaluating it. However, it will evaluate any contained expressions,
such as pointers, needed to determine the entity to which you are referring.
whatis_command
: whatis whatis_expression
The following example uses the whatis
command to determine the
storage representation for the data member _classification
:
(ladebug) whatis sun->_classification
const enum StellarClass Star::_classification
(ladebug) whatis StellarClass
enum StellarClass {O, B, A, F, G, K, M, R, N, S}
(ladebug) print sun->_classification
G
The whereis
command lists all declarations of a variable and
each declaration's fully qualified scope information.
The scope information of a variable usually consists of the name of the source
file that contains the function in which the variable is declared, the name of that
function, and the name of the variable. The components of the scope information are
separated by back-quotes (`).
whereis_command
: whereis whereis_name
| whereis whereis_string
whereis_name
: identifier_or_typedef_name
| ( identifier_or_typedef_name )
whereis_string
: string
You can use the whereis
command with the
whereis_name
to obtain information needed to differentiate overloaded identifiers that are in different units, or within
different routines in the same unit. The following example shows how to set
breakpoints in two C++ methods, both named print
:
(ladebug) whereis print
"solarSystemSrc/base_class_includes/heavenlyBody.h"`HeavenlyBody::print(unsigned int)
"solarSystemSrc/derived_class_includes/planet.h"`Moon::print(unsigned int)
"solarSystemSrc/derived_class_includes/planet.h"`Planet::print(unsigned int)
"solarSystemSrc/derived_class_includes/star.h"`Star::print(unsigned int)
(ladebug) stop in "solarSystemSrc/derived_class_includes/planet.h"`Planet::print
Select from
----------------------------------------------------
1 planet.h containing Moon
2 planet.h containing Moon
3 planet.h containing __dt__6PlanetXv
4 None of the above
----------------------------------------------------
1
[#2: stop in virtual void Planet::print(unsigned int) ]
(ladebug) stop in "solarSystemSrc/derived_class_includes/star.h"`Star::print
Select from
----------------------------------------------------
1 star.h containing O
2 star.h containing O
3 None of the above
----------------------------------------------------
1
[#3: stop in virtual void Star::print(unsigned int) ]
See also the which
command for another example of the
whereis
command.
If you are not sure how to spell a symbol, you can use the whereis
command with the whereis_string
to search the symbol table for
the regular expression represented by the quoted string. All symbols that match
the rules of the regular expression are displayed in ascending order. For
example:
(ladebug) whereis planet
Symbol not found
(ladebug) whereis "[Pp]lanet"
"solarSystemSrc/derived_class_includes/planet.h"`Moon::Moon(char*, Megameters, Kilometers, class Planet*)
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet::Planet(char*, Megameters, class HeavenlyBody*)
"solarSystemSrc/derived_class_includes/planet.h"`Planet::Planet(char*, Megameters, class HeavenlyBody*)
"solarSystemSrc/derived_class_includes/planet.h"`Planet::print(unsigned int)
"solarSystemSrc/derived_class_includes/planet.h"`__INTER__Moon_Moon_Orbit_Planet_Xv
"solarSystemSrc/derived_class_includes/planet.h"`__INTER__Planet_Planet_Orbit_Xv
"solarSystemSrc/derived_class_includes/planet.h"`__dt__6PlanetXv
__T_6Planet
__cxxexsig6Planet
__vtbl_5Orbit6Planet
__vtbl_5Orbit6Planet4Moon
__vtbl_6Planet
solarSystemSrc/derived_class_includes/planet.h
solarSystemSrc/derived_class_includes/planet.h
solarSystemSrc/derived_class_includes/planet.h
solarSystemSrc/planet.cxx
(ladebug) whereis "^Planet$"
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet::Planet(char*, Megameters, class HeavenlyBody*)
(ladebug) whereis Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet::Planet(char*, Megameters, class HeavenlyBody*)
(ladebug) which Planet
"solarSystemSrc/derived_class_includes/planet.h"`Planet
(ladebug) whatis Planet
class Planet : HeavenlyBody, Orbit {
Planet(char*, Megameters, class HeavenlyBody*);
virtual void print(unsigned int);
}
You can use the $symbolsearchlimit
debugger
variable to specify the maximum number of symbols that are returned by the
whereis
command for a regular expression
search. The default value for the $symbolsearchlimit
variable is
100; a value of 0 indicates no limit.
Use the which
command to determine which declaration an identifier
resolves to. The which
command shows the fully qualified scope
information for the instance of the specified expression in the current scope.
The scope information of a variable usually consists of the name of the source
file that contains the function in which the variable is declared, the name of that
function, and the name of the variable. The components of the scope information are
separated by back-quotes (`).
which_command
: which which_name
which_name
: identifier_or_typedef_name
| ( identifier_or_typedef_name )
The following example shows how to use the whereis
and
which
commands to determine a variable's scope:
(ladebug) where 4
>0 0x1200047dc in ((Planet*)0x140004060)->Planet::print(i=2) "solarSystemSrc/planet.cxx":19
#1 0x12000422c in ((HeavenlyBody*)0x140004060)->HeavenlyBody::printBodyAndItsSatellites(i=2) "solarSystemSrc/heavenlyBody.cxx":62
#2 0x120004254 in ((HeavenlyBody*)0x140002c00)->HeavenlyBody::printBodyAndItsSatellites(i=1) "solarSystemSrc/heavenlyBody.cxx":68
#3 0x120004110 in main() "solarSystemSrc/main/solarSystem.cxx":120
(ladebug) which i
"solarSystemSrc/planet.cxx"`Planet::print(unsigned int)`i
(ladebug) assign i = 10
(ladebug) print i
10
(ladebug) whereis i
"solarSystemSrc/heavenlyBody.cxx"`HeavenlyBody::printBodyAndItsSatellites(unsigned int)`i
"solarSystemSrc/heavenlyBody.cxx"`HeavenlyBody::printBodyAndItsSatellites(unsigned int)`i
"solarSystemSrc/heavenlyBody.cxx"`HeavenlyBody::satelliteNumber(class HeavenlyBody*)`i
"solarSystemSrc/main/solarSystem.cxx"`main`i
"solarSystemSrc/main/solarSystem.cxx"`printBiggestMoons`i
"solarSystemSrc/main/solarSystem.cxx"`trackBiggestMoons(class Moon*)`i
"solarSystemSrc/planet.cxx"`Moon::print(unsigned int)`i
"solarSystemSrc/planet.cxx"`Planet::print(unsigned int)`i
"solarSystemSrc/star.cxx"`Star::print(unsigned int)`i
(ladebug) func HeavenlyBody::printBodyAndItsSatellites
void HeavenlyBody::printBodyAndItsSatellites(unsigned int) in solarSystemSrc/heavenlyBody.cxx line No. 62:
62 print(i);
(ladebug) which i
"solarSystemSrc/heavenlyBody.cxx"`HeavenlyBody::printBodyAndItsSatellites(unsigned int)`i
(ladebug) print i
2
8.4.11 Notes on C++ Debugging
The following sections describe the debugger commands specific to debugging C++
programs.
8.4.11.1 Setting the Class Scope Using the class Command
The debugger maintains the concept of a current context in which to perform
lookup of program variable names. The current context includes a file scope
and either a function scope or a class scope. The debugger automatically
updates the current context when program execution suspends.
The class
command lets you set the scope to a class in the program
you are debugging:
c++_look_around_command
: class [ class_name ]
If class_name
is not specified, the class
command displays the
current class context.
Setting the class scope nullifies the function scope and vice versa. To return to the
default (current function) scope, use the command func 0
.
Explicitly setting the debugger's current context to a class enables you to view a class
to:
- Set a breakpoint in a member function
- Print static data members
- Examine any data member's type
After the class scope is set, you can set breakpoints in the class's
member functions and examine data without explicitly mentioning the class name.
If you do not want to affect the current context, you can use the scope
resolution operator (::) to access a class whose members are not currently
visible. Use the class
command without an argument to display the
current class scope. Specify an argument to change the class scope. After the
class scope is set, refer to members of the class by omitting the
classname::
prefix.
The following example shows the use of the class
command to set the
class scope to List<Node>
in order to make member function append
visible
so a breakpoint can be set in append
:
(ladebug) stop in append
Symbol "append" is not defined.
append has no valid breakpoint address
Warning: Breakpoint not set
(ladebug) class List<Node>
class List<Node> {
List(void);
~List(void);
void append(class Node* const);
void print(void);
class Node* _firstNode;
}
(ladebug) stop in append
[#1: stop in void List<Node>::append(class Node* const) ]
8.4.11.2 Displaying Class Information
The whatis
and print
commands display
information on a class.
Use the whatis
command to display static information about the classes.
Use the print
command to view dynamic information about class objects.
The whatis
command displays the class type declaration, including the
following:
- Data members
- Member functions
- Constructors
- Destructors
- Static data members
- Static member functions
For classes that are derived from other classes, the data members and member
functions inherited from the base class are not displayed. Any member functions
that are redefined from the base class are displayed.
The print
command lets you display the value of data members and
static members. Information regarding the public, private, or protected status
of class members is not provided, because the debugger relaxes the related
access rules to be more helpful to users.
The type signatures of member functions, constructors, and destructors are
displayed in a form that is appropriate for later use in resolving references
to overloaded functions.
The following example shows the whatis
and print
commands in
conjunction with a class:
(ladebug) list 43: 12
43 // Compound Node - contains integer and float data items
44 //
45 class CompoundNode : public IntNode {
46 public:
47 CompoundNode (float fdata, int idata);
48
49 void printNodeData() const;
50
51 private:
52 float _fdata;
53 };
54
(ladebug) whatis CompoundNode
class CompoundNode : IntNode {
CompoundNode(float, int);
virtual void printNodeData(void);
float _fdata;
}
(ladebug) whatis CompoundNode::CompoundNode
CompoundNode::CompoundNode(float, int)
(ladebug) stop in CompoundNode::printNodeData
[#1: stop in virtual void CompoundNode::printNodeData(void) ]
(ladebug) run
The list is:
Node 1 type is integer, value is 1
[1] stopped at [virtual void CompoundNode::printNodeData(void):109 0x12000238c]
109 cout << " type is compound, value is ";
(ladebug) print _fdata
12.3450003
8.4.11.3 Displaying Object Information
The whatis
and print
commands display information on
instances of classes (objects). Use the whatis
command to display the
class type of an object. Use the print
command to display the current
value of an object.
You can also display individual object members using the member access
operators, period (.), and right arrow (->) in a print
command.
You can use the scope resolution operator (::) to refer to global variables,
to refer to hidden members in base classes, to explicitly refer to a member
that is inherited, or to name a member hidden by the current context.
When you are in the context of a nested class, you must use the scope
resolution operator to access members of the enclosing class.
The following example shows how to use the whatis
and
print
commands to display object information:
(ladebug) whatis this
const class CompoundNode* const this
(ladebug) whatis *this
class CompoundNode : IntNode {
CompoundNode(float, int);
virtual void printNodeData(void);
float _fdata;
}
(ladebug) print *this
class CompoundNode {
_fdata = 12.3450003;
_data = 2; // class IntNode
_nextNode = 0x140004030; // class IntNode::Node
}
(ladebug) print _fdata, _data
12.3450003 2
(ladebug) print this->_fdata, this->_data
12.3450003 2
8.4.11.4 Displaying Static and Dynamic Type Information
When displaying object information for C++ class pointers or references, you
have the option of viewing either static type information or
dynamic type information.
The static type of a class pointer or reference is its type as defined in the
source code, and thus cannot change. The dynamic type is the type of the object
being referenced, before any casts were made to that object, and thus may change
during program execution.
The debugger provides a debugger variable,
$usedynamictypes
, which allows
you to control which form of the type information is displayed. The
default value for this variable is true (1), which indicates that the dynamic
type information is displayed. Setting this variable to false (0)
instructs the debugger to display static type information. The output of the
print, trace, tracei,
and
whatis
commands are affected.
The display of dynamic type information is supported for C++ class pointers
and references. All other types display static type information. In
addition, if the dynamic type of an object cannot be determined, the debugger
defaults to the use of static type information.
This debugger functionality does not relax the C++ visibility rules regarding
object member access through a pointer/reference (only members of the static
type are accessible). For more information about the C++ visibility rules, see
The Annotated C++ Reference Manual (by Margaret E. Ellis and Bjarne
Stroustrup, 1990, Addison-Wesley Publishing Company).
In order for dynamic type information to be displayed, the object's
static type must have at least one virtual function defined as part of its
interface (either one it introduced or one it inherited from a base class). If
no virtual functions are present for an object, only the static type
information for that object is available for display.
The following example shows debugger output with $usedynamictypes
set to 0 (false):
(ladebug) print $usedynamictypes
0
(ladebug) whatis *this
class HeavenlyBody {
HeavenlyBody(char*);
const char* name(void);
void addSatellite(class HeavenlyBody*);
unsigned int satelliteNumber(class HeavenlyBody*);
virtual void print(unsigned int);
void printBodyAndItsSatellites(unsigned int);
const char* const _name;
class HeavenlyBody* _innerNeighbor;
class HeavenlyBody* _outerNeighbor;
class HeavenlyBody* _firstSatellite;
class HeavenlyBody* _lastSatellite;
}
(ladebug) print *this
class HeavenlyBody {
_name = 0x120002088="Moon";
_innerNeighbor = 0x0;
_outerNeighbor = 0x0;
_firstSatellite = 0x0;
_lastSatellite = 0x0;
}
The following example displays debugger output with $usedynamictypes
set to 1 (true). The output is for the same object as the previous example,
at the same point in program execution.
(ladebug) print $usedynamictypes
1
(ladebug) whatis *this
class Moon : Planet {
Moon(char*, Megameters, Kilometers, class Planet*);
Kilometers radius(void);
virtual void print(unsigned int);
const Kilometers _radius;
}
(ladebug) print *this
class Moon {
_radius = 1738;
_name = 0x120002088="Moon"; // class Planet::HeavenlyBody
_innerNeighbor = 0x0; // class Planet::HeavenlyBody
_outerNeighbor = 0x0; // class Planet::HeavenlyBody
_firstSatellite = 0x0; // class Planet::HeavenlyBody
_lastSatellite = 0x0; // class Planet::HeavenlyBody
_primary = 0x1400040c0; // class Planet::Orbit
_distance = 384; // class Planet::Orbit
_name = 0x1400058f0="Earth 1"; // class Planet::Orbit
}
8.4.11.5 Displaying Virtual and Inherited Class Information
When you use the print
command to display information on an instance
of a derived class, the debugger displays both the new class members as well as the
members inherited from a base class. Pointers to members of a class are not supported.
When you use the print
command to display the format of C++ classes,
the class name (or structure/union name) is displayed at the top of the output.
Data members of a class that are inherited from another class are commented
using a double slash (//). Only those data members that are inherited within
the current class being printed are commented.
The following example shows how the debugger uses C++ style comments to
identify inherited class members. In the example, class CompoundNode inherits from class IntNode,
which inherits from class Node. When printing a class CompoundNode object, the data member
_data is commented with "// class IntNode", signifying that it is inherited from
class IntNode. The member _nextNode is commented with "// class IntNode::Node", showing that
it is inherited from class IntNode, which inherits it from class Node. This
commenting is also provided for C++ structs.
(ladebug) where 3
>0 0x12000226c in ((Node*)0x140004000)->Node::Node() "x_list.cxx":77
#1 0x1200022b8 in ((IntNode*)0x140004000)->IntNode::IntNode(data=2) "x_list.cxx":88
#2 0x120002358 in ((CompoundNode*)0x140004000)->CompoundNode::CompoundNode(fdata=12.345000267028809, idata=2) "x_list.cxx":101
(ladebug) whatis *this
class Node {
Node(void);
virtual void printNodeData(void);
class Node* getNextNode(void);
void setNextNode(class Node*);
class Node* _nextNode;
}
(ladebug) print *this
class Node {
_nextNode = 0x0;
}
(ladebug) up 1
>1 0x1200022b8 in ((IntNode*)0x140004000)->IntNode::IntNode(data=2) "x_list.cxx":88
88 IntNode::IntNode(int data) : _data(data)
(ladebug) whatis *this
class IntNode : Node {
IntNode(int);
virtual void printNodeData(void);
int _data;
}
(ladebug) print *this
class IntNode {
_data = 0;
_nextNode = 0x0; // class Node
}
(ladebug) up 1
>2 0x120002358 in ((CompoundNode*)0x140004000)->CompoundNode::CompoundNode(fdata=12.345000267028809, idata=2) "x_list.cxx":101
101 CompoundNode::CompoundNode(float fdata, int idata)
(ladebug) whatis *this
class CompoundNode : IntNode {
CompoundNode(float, int);
virtual void printNodeData(void);
float _fdata;
}
(ladebug) print *this
class CompoundNode {
_fdata = 0;
_data = 0; // class IntNode
_nextNode = 0x0; // class IntNode::Node
}
If two members in an object have the same name but different base
class types (multiple inheritance), you can refer to the members using the following syntax:
object.class::member
or
object->class::member
This syntax is more effective than using the object.member
and
object->member
syntaxes, which can be ambiguous. In all cases,
the debugger uses the C++ language rules as defined in
The Annotated C++ Reference Manual to determine which member you are
specifying.
The following example shows a case in which the expanded syntax can be used:
(ladebug) print *jupiter
class Planet {
_name = 0x1200020a8="Jupiter"; // class HeavenlyBody
_innerNeighbor = 0x140004180; // class HeavenlyBody
_outerNeighbor = 0x1400044e0; // class HeavenlyBody
_firstSatellite = 0x140004300; // class HeavenlyBody
_lastSatellite = 0x140004480; // class HeavenlyBody
_primary = 0x140002c00; // class Orbit
_distance = 778330; // class Orbit
_name = 0x140005a30="Sol 5"; // class Orbit
}
(ladebug) print jupiter->_name
Overloaded Values
0x140005a30="Sol 5"
0x1200020a8="Jupiter"
(ladebug) print jupiter->HeavenlyBody::_name
0x1200020a8="Jupiter"
(ladebug) print jupiter->Orbit::_name
0x140005a30="Sol 5"
8.4.11.6 Member Functions on the Stack Trace
The implicit this
pointer, which is a part of all nonstatic member functions,
is displayed as the address on the stack trace. The class type of the object
is also given.
Sometimes the debugger does not see class type names with internal linkage. When
this happens, the debugger issues the following error message:
Name is overloaded.
Trying to examine an inlined member function that is not called results in the
following error:
Member function has been inlined.
The debugger will report this error regardless of the setting of the -noinline_auto compilation
flag. As a workaround, include a call to
the given member function somewhere in your program. (The call does not need to be executed.)
If a program is not compiled with the -g
flag, a breakpoint set on an inlined member function may confuse the debugger.
8.4.11.7 Resolving Ambiguous References to Overloaded Functions
In most cases, the debugger works with one specific function at a time. In the
case of overloaded function names, you must specify the desired overloaded
function. Following are two ways to resolve references to overloaded function names,
both under the control of the
$overloadmenu
debugger variable (the
default setting of this debugger variable is 1):
- Choose the correct reference from a selection menu.
If the $overloadmenu
variable is set to 1 (the default), whenever you specify a function name that
is overloaded,
a menu appears with all the possible functions; you must select from this menu.
In this example, a breakpoint is set in foo
,
which is overloaded:
(ladebug) set $overloadmenu = 1
(ladebug) stop in C::foo
Select from
----------------------------------------------------
1 int C::foo(double*)
2 void C::foo(float)
3 void C::foo(int)
4 void C::foo(void)
5 None of the above
----------------------------------------------------
1
[#10: stop in int C::foo(double*) ]
- Enter the function name with its full type signature.
If you prefer this method, set the
$overloadmenu
variable to 0. To see the
possible type signatures for the overloaded function, first display all the
declarations of an overloaded function by using the whatis
command.
You cannot select a version of an overloaded function that has a type signature
containing ellipsis points (...). Pointers to functions with type signatures that
contain the list parameter or ellipsis points are not supported.
Use the specific function type signature to refer to the desired version of the
overloaded function. If a function has no parameter, include the void parameter as
the function's type signature. In the following example, the function context is
set to foo(double *)
, because foo
is overloaded:
(ladebug) func foo
Error: foo is overloaded
(ladebug) func foo(double *)
int C::foo(double*) in x_overload.cxx line No. 25:
25 int C::foo(double *) { return state;}
8.4.11.8 Advanced Program Information —Verbose Mode
By default, the debugger gives no information on virtual base class pointers for the following:
- Derived classes
- Virtual pointer tables for virtual functions
- Compiler-generated function members
- Compiler-generated temporary variables
- Implicit parameters in member functions
By setting the $verbose
debugger variable to 1, you can request that this
information be printed in subsequent debugger responses. When the $verbose
debugger variable is set to 1
and you display the contents of a class using the
whatis
command, several of the class members listed
are not in the source code of the original class definition. The following line shows specific output from the
whatis
command for one of the additional members:
(ladebug) whatis CompoundNode::__vptr
(array [subrange 0 ... 0 of int] of union {
void <member function>(void)* fptr;
int ioffset;
})* CompoundNode::__vptr
The __vptr
variable contains the addresses of all virtual functions associated
with the class. The compiler generates several other class members for internal use.
The compiler generates additional parameters for nonstatic member functions. When the
$verbose
debugger variable is set to 1, these extra parameters are displayed
as part of each member function's type signature. If you specify a version of an overloaded
function by entering its type signature and the variable is set to 1, you must include
these parameters. Do not include these parameters if the variable is set to 0.
When the $verbose
variable is set to 1, the output of the
dump
command includes not only standard program variables but also compiler-generated temporary variables.
The following example prints class information using the whatis
command under different settings of the $verbose
variable:
(ladebug) set $verbose = 0
(ladebug) whatis CompoundNode
class CompoundNode : IntNode {
CompoundNode(float, int);
virtual void printNodeData(void);
float _fdata;
}
(ladebug) set $verbose = 1
(ladebug) whatis CompoundNode
class CompoundNode : IntNode {
(array [subrange 0 ... 0 of int] of union {
void <member function>(void)* fptr;
int ioffset;
})* __vptr;
CompoundNode(class CompoundNode* const, float, int);
virtual void printNodeData(const class CompoundNode* const);
float _fdata;
}
This section discusses the following topics:
You can use the following commands to read arbitrary memory locations
in your program:
machinecode_level_command
: examine_command
: search_command
examine_command
: address_expression / [ count ] [ mode ]
| address_expression ? [ count ] [ mode ]
| address_expression , address_expression / [ mode ]
search_command
: address_expression / [ count ] search_mode value mask
| address_expression ? [ count ] search_mode value mask
| address_expression , address_expression / search_mode value mask
count
: integer_constant
mode
: d Print a short (2 byte) word in decimal
| dd Print a 32-bit (4 byte) decimal display
| D Print a long (8 byte) word in decimal
| u Print a short (2 byte) word in unsigned decimal
| uu Print a 32-bit (4 byte) unsigned decimal display
| U Print a long (8 byte) word in unsigned decimal
| o Print a short (2 byte) word in octal
| oo Print a 32-bit (4 byte) octal display
| O Print a long (8 byte) word in octal
| x Print a short (2 byte) word in hexadecimal
| xx Print a 32-bit (4 byte) hexadecimal display
| X Print a long (8 byte) word in hexadecimal
| b Print a byte in hex
| c Print a byte as a character
| s Print a string of characters (a C-style string ending in null)
| C Print a wide character as a character
| S Print a null terminated string of wide characters
| f Print a single precision real number
| g Print a double precision real number
| L Print a long double precision real number
| i Disassemble machine instructions
search_mode
: m 32-bit search mode
| M 64-bit search mode
value
: integer_constant
mask
: integer_constant
The first examine_command
displays the count
number of memory values in the requested format,
starting at address_expression
. If count
is not specified, 1 is assumed.
The count value must be a positive value.
If you want to see memory values leading up to the address_expression
,
use the second examine_command
. The second examine_command
displays
count
number of memory values in
the requested format ending at the address_expression
. If count
is not specified,
1 is assumed. The count value must be a positive value.
The third examine_command
displays memory values in the requested format starting at
the smaller of the two address_expressions
and ending at the larger
address_expression
.
You can display stored values in the following formats by specifying mode
. If mode
is not specified, the mode used in the previous /
command is assumed. If no previous /
command exists, X
is assumed.
When disassembling machine instructions, use the $regstyle
variable to
customize how the registers are displayed.
The search_commands
allow you to search memory. Use the address_expression
and
count
to determine the range of
memory to search. Use the search_mode
to specify whether you want to search 32-bit or 64-bit chunks.
The debugger will start
at the specified starting address and read a chunk of memory (either 32 or 64 bits in size) and apply
the mask
and comparison on that chunk of memory. For example, if you want to search memory for a particular
instruction
or search an array of either integer or floating-point values, the 32-bit search would be efficient
because machine instructions
and integer and floating-point data types are 32 bits in length.
Use the value
to specify
the memory value to seek.
Use the mask
to specify those bits that
must match the same bits in the specified value. To ensure that a possible match will be found, the
debugger applies the mask
to the input
value prior to starting the search, to remove any bits that could prevent a match from occurring.
Then, for each memory location searched,
the debugger applies the mask
to the memory value and then compares it with this new
input value.
If a match is found, then the address and memory value are displayed.
For example, suppose the user wants to check an array of 100 integers in memory to see if any values
are NULL (0):
(ladebug) array,&(array[99])/m 0x0 0xfffffff
0x1400005d0: 0x00000000
Suppose the user wants to find the first occurrence of the particular value
0x55 in the thousand-element array data
:
(ladebug) data/1000m 0x55 0xffffffff
0x1400002c4: 0x00000055
Use the debugger variable $memorymatchall
to cause the debugger to output all matches in the
specified range. For example, suppose you want to find all occurrences of numbers
ending in the value 0x55 in the same array:
(ladebug) set $memorymatchall = 1
(ladebug) data/1000m 0x55 0xff
0x1400002c4: 0x00000055
0x1400006c4: 0x00000155
0x140000ac4: 0x00000255
0x140000ec4: 0x00000355
8.5.2 Machine-Level Debugging
The debugger lets you debug your programs at the machine-code level as well as at the
source-code level. Using debugger commands, you can examine and edit values in memory, print
the values of all machine registers, and step through program execution one machine instruction
at a time.
Only those users familiar with machine-language programming and executable file code structure
will find low-level debugging useful.
For more information on machine-level debugging, see
Machine-Level Debugging in Part III.
Most programs consist of a main image and shared libraries. Use the listobj
command to show them. Most programmers will not need to use
the readsharedobj
or delsharedobj
commands unless they use the -nosharedobjs
option on the command line.
shared_library_command
: listobj
| readsharedobj filename
| delsharedobj filename
Use the listobj
command to list all loaded objects,
including the main image and the shared libraries.
For each object, the information listed consists of the full
object name (with pathname) and the starting and ending
addresses for the .text
, .data
, and .bss
sections.
Use the readsharedobj
command to read in the symbol table
information for the specified shared object. This object
must be a shared library or loadable kernel module.
You can use the command only when you specify the debuggee; that is, either
the debugger has been invoked with it, or
the debuggee was loaded by the load
command.
Conversely, use the delsharedobj
command to remove the
symbol table information for the shared object from the debugger.
Chapter 9Modifying the Process
In addition to the normal side effects of evaluating expressions, including
calls, you can explicitly modify the memory of the current process and
also modify the actual loadable file (either executable file or shared
library) that has been mapped into memory:
modifying_command
: assign target = expression
| patch target = expression
target
: unary_expression
The following sections discuss these commands.
Use the assign
command to change the value associated with a variable,
memory address, or expression that is accessible according to the scope and visibility
rules of the language. The expression can be any expression that is valid in the
current context.
The following example shows how to deposit the value 5
into the data
member _data
of a C++ object:
(ladebug) print node->_data
2
(ladebug) assign node->_data = 5
(ladebug) print node->_data
5
The following example shows how to change the value associated with a variable
and the value associated with an expression:
(ladebug) print *node
class CompoundNode {
_fdata = 12.3450003;
_data = 5; // class IntNode
_nextNode = 0x0; // class IntNode::Node
}
(ladebug) assign node->_data = -32
(ladebug) assign node->_fdata = 3.14159 * 4.2 * 4.2
(ladebug) assign node->_nextNode = _firstNode
(ladebug) print *node
class CompoundNode {
_fdata = 55.4176483;
_data = -32; // class IntNode
_nextNode = 0x140004c00; // class IntNode::Node
}
For C++, use the assign
command to modify static and object data
members in a class, and variables declared as reference types, type const, or
type static. You cannot change the address referred to by a reference type, but
you can change the value at that address.
assign [classname::]member = ["filename"] `expression
assign [object.]member = ["filename"] `expression
NOTE: Do not use the assign
command to change the PC.
When you change the PC, no adjustment to the contents
of registers or memory is made. This means that if
you adjust the PC forward, the skipped instructions are
not executed and any changes they would have made do not occur.
If you adjust the PC
backward, the instructions you backed up over are not
undone, and any changes they made will be in effect when
execution continues again.
Because most instructions change registers or memory in
ways that can impact the meaning of the application, changing
the PC is very likely to cause your application to do
incorrect calculations and arrive at the wrong answer.
Access violations and other errors and signals may
result from changing the value in the PC.
The assign Command in
Machine-Level Debugging
You can use the assign
command to alter the contents of memory
specified by an address as shown in the following example:
(ladebug) print *(int *)(5368717328)
-32
(ladebug) assign *(int *)(5368717328) = 1024
(ladebug) print *(int *)(5368717328)
1024
See Machine-Level Debugging for
more information.
Use the patch
command to correct bad data or instructions in executable disk
files. You can patch the text, initialized data, or read-only data areas. You cannot patch
the bss
segment, or stack and register locations,
because they do not exist on disk files.
Use this command exclusively when you need to change the on-disk binary. Use the
assign
command when you need only to modify debuggee memory. If the
image is executing when you issue the patch
command, the corresponding
location in the debuggee address space is updated as well. (The debuggee is
updated regardless of whether the patch to disk succeeded, as long as the
source and destination expressions could have been processed by an assign
command.) If your program is loaded but not yet started, the patch to disk
is performed without the corresponding assign to memory.
(ladebug) run
[1] stopped at [int main(void):24 0x120001324]
24 return 0;
(ladebug) patch i = 10
0x1400000d0 = 10
(ladebug) patch j = i + 12
0x1400000d8 = 22
(ladebug)
NOTE: When you use the patch
command, the original binary
is not overwritten, but is saved with the string ~backup
appended
to the file name.
This allows you to revert to the original binary if necessary. A file with the
string ~temp
appended
to the file name may also be created. You can delete it after the
debugging session is over.
Chapter 10Continuing Execution of the Process
Before continuing the process, decide whether or not to make a
snapshot, in case you want to revert
to that snapshot state and try a different set of steps.
After creating the snapshot, use the following commands to continue executing the
program:
continue_command
: step_into_command
| step_over_command
| step_out_of_command
| cont_command
| cont_from_place_command
10.1 The step and stepi
Commands
Use the step
command to execute a line of source code. When the
line being stepped contains a function call, the step
command
steps into the function and stops at the first executable statement.
Use the stepi
command to step into the next machine instruction. When
the instruction contains a function call, the stepi
command steps
into the function being called.
NOTE: If the instruction is a load locked instruction,
special rules apply for stepi
.
For multithreaded applications, use these commands to step the current thread
while putting all other threads on hold.
If you supply the optional expression argument, the debugger evaluates the
expression as a positive integer that specifies the number of times to execute
the command. The expression can be any expression that is valid in the current
context.
step_into_command
: step [ step_number ]
| stepi [ step_number ]
step_number
: expression
In the following example, two step
commands
continue executing a C++ program:
(ladebug) step
stopped at [void List<Node>::append(class Node* const):151 0x120001dd8]
151 Node* currentNode = _firstNode;
(ladebug) step
stopped at [void List<Node>::append(class Node* const):152 0x120001de0]
152 while (currentNode->getNextNode())
The following example shows stepping by
instruction (stepi
). To see stepping into calls, see the
next
example.
(ladebug) $curpc/4i
void List<Node>::append(class Node* const): x_list.cxx
*[line 156, 0x120001e2c] ldq r26, 0(sp)
[line 156, 0x120001e30] ldq r9, 8(sp)
[line 156, 0x120001e34] lda sp, 32(sp)
[line 156, 0x120001e38] ret r31, (r26), 1
(ladebug) stepi
stopped at [void List<Node>::append(class Node* const):156 0x120001e30] ldq r9, 8(sp)
(ladebug) stepi
stopped at [void List<Node>::append(class Node* const):156 0x120001e34] lda sp, 32(sp)
(ladebug) stepi
stopped at [void List<Node>::append(class Node* const):156 0x120001e38] ret r31, (r26), 1
(ladebug) stepi
stopped at [int main(void):190 0x120002560] ldah gp, 8192(r26)
Use the next
command to execute a line of source code. When the next
line to be executed contains a function call, the next
command executes
the function being called and stops the process at the line immediately after the function
call.
Use the nexti
command to execute a machine instruction. When the
instruction contains a function call, the nexti
command executes the
function being called and stops the process at the instruction immediately after the
call instruction.
For multithreaded applications, use these commands to move the current thread
while putting all other threads on hold.
If you supply the optional expression argument, the debugger evaluates the
expression as a positive integer that specifies the number of times to execute
the command. The expression can be any expression that is valid in the current
context.
step_over_command
: next [ step_number ]
| nexti [ step_number ]
step_number
: expression
For example:
(ladebug) next
stopped at [int main(void):192 0x120002568]
192 CompoundNode* cNode1 = new CompoundNode(3.1415, 7);
(ladebug) next
stopped at [int main(void):193 0x1200025e8]
193 nodeList.append(cNode1);
The following example shows the difference between
stepi
and nexti
over the same call:
(ladebug) cont
[2] stopped at [void List<Node>::append(class Node* const):152 0x120001de4]
152 while (currentNode->getNextNode())
(ladebug) $curpc/4i
void List<Node>::append(class Node* const): x_list.cxx
*[line 152, 0x120001de4] ldq r27, -32584(gp)
[line 152, 0x120001de8] jsr r26, (r27), Node::getNextNode
[line 152, 0x120001dec] ldah gp, 8192(r26)
[line 152, 0x120001df0] lda gp, 25940(gp)
(ladebug) stepi
stopped at [void List<Node>::append(class Node* const):152 0x120001de8] jsr r26, (r27), Node::getNextNode
(ladebug) stepi
stopped at [class Node* Node::getNextNode(void):81 0x120002284] bis r31, r16, r1
(ladebug) cont
[2] stopped at [void List<Node>::append(class Node* const):152 0x120001de4]
152 while (currentNode->getNextNode())
(ladebug) nexti
stopped at [void List<Node>::append(class Node* const):152 0x120001de8] jsr r26, (r27), Node::getNextNode
(ladebug) nexti
stopped at [void List<Node>::append(class Node* const):152 0x120001dec] ldah gp, 8192(r26)
10.3 The return Command
Use the return
command without an argument to continue execution of the
current function until it returns to its caller. If you include a function
name, execution continues until control is returned to the specified function.
The function must be active on the call stack.
step_out_of_command
: return
| return [qual_symbol_opt]
qual_symbol_opt
: expression
| qual_symbol_opt ` expression
In the following example, the next
command is
stepping through process execution in the
append
method. The return
command
finishes the append
method and returns control to the caller.
(ladebug) next
stopped at [void List<Node>::append(class Node* const):154 0x120001e14]
154 currentNode->setNextNode(node);
(ladebug) return
stopped at [int main(void):193 0x1200025f8]
193 nodeList.append(cNode1);
The return
command is sensitive to the user's location in the call stack.
Suppose function A calls function B, which calls function C. Execution has stopped in
function C, and you entered the up
command, so you were now in function B,
at the point where it called function C. Using the return
command here
would return you to function A, at the point where function A called function B.
Functions B and C will have completed execution.
10.4 The cont Command
Use the cont
command without a parameter value to resume process
execution until a breakpoint, a signal, an error, or normal process termination is
encountered. Specify a signal parameter value to send an operating system
signal to the process.
cont_command
: cont [ in loc ]
| cont [ signal ] [ to_source_line ]
| number_expression cont [ signal ]
| conti to address_expression
to_source_line
: to [filename_string :] line_number
number_expression
: expression
signal
: integer_constant
| signal_name
When you use the cont
command, the debugger resumes execution of the
entire process.
In the following example, a cont
command resumes process execution
after it was suspended by a breakpoint:
(ladebug) list $curline - 5: 10
188
189 CompoundNode* cNode = new CompoundNode(12.345, 2);
190 nodeList.append(cNode);
191
192 CompoundNode* cNode1 = new CompoundNode(3.1415, 7);
> 193 nodeList.append(cNode1);
194
195 nodeList.append(new IntNode(3));
196
197 IntNode* newNode2 = new IntNode(4);
(ladebug) stop at 198
[#3: stop at "x_list.cxx":198 ]
(ladebug) cont
[3] stopped at [int main(void):198 0x1200026fc]
198 nodeList.append(newNode2);
The signal parameter value can be either a signal number or a string name (for
example, SIGSEGV). The default is 0, which allows the process to continue
execution without specifying a signal. If you specify a signal parameter value,
the process continues execution with that signal.
Use the in
argument to continue until the named function is reached.
The function name must be valid. If the function name is overloaded and you do not
resolve the scope of
the function in the command line, the debugger prompts you with the list of
overloaded functions bearing that name from which to choose.
Use the to
parameter value to resume execution and then
halt when the specified source line is reached.
Use one of the following forms of the optional to
parameter:
filename_string:line_number
, which explicitly identifies both the source
file and the line number where execution is to be halted.
line_number
, a positive numeric, which indicates the line number of
the current source file where execution is to be halted.
You can repeat the cont
command
(n +1) times by entering
n cont
.
You can set a one-time breakpoint on an instruction address before continuing
by entering conti to address_expression
.
10.5 The goto Command
The goto
command is intended for advanced users who want
to skip over the execution of a portion of source code. In general, its usage is not
recommended.
cont_from_place_command
: goto line_expression
line_expression
: expression
Chapter 11Using Snapshots as an Undo Mechanism
You can save the current state of the debuggee process in a snapshot, and later
revert to that state and try a different set of steps. Conceptually speaking,
this feature is similar to the undo function in text editors, except that with
snapshots you have control of the granularity of each undo. See the
Introduction for a quick overview.
snapshot_command
: save_snapshot_command
| clone_snapshot_command
| show_snapshot_command
| delete_snapshot_command
The following sections discuss these commands and address the
limitations of snapshots.
11.1 The save snapshot Command
Use the save snapshot
command to save the state of the current process
in a snapshot. Snapshots are numbered sequentially starting from 1.
save_snapshot_command
: save snapshot
In the following example, the first line of the save snapshot
message
shows the snapshot_number (1), the time it is saved,
and the ID number of the process that implements the snapshot.
The next two lines show the status of the snapshot.
(ladebug) save snapshot
# 1 saved at 13:27:54 (PID: 29077).
stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
11.2 The clone snapshot Command
Use the clone snapshot
command to revert the state of the
debuggee process to that of a previously saved snapshot. By doing this,
you can conveniently return to the state saved in the snapshot as opposed
to rerunning the process and re-entering the debugger command sequence
that brought you to that state.
Note that rerun
and clone snapshot
are different in that
rerun
always executes the process from the beginning, whereas
clone snapshot
does not execute the process at all; it simply
duplicates the saved snapshot (using a mechanism similar to the fork
system
call) and behaves as though the process execution has stopped at the point when the
snapshot was saved.
The clone snapshot
command clones the snapshot specified by
snapshot_id. If no snapshot_id is
specified, the most-recently saved existing snapshot is cloned.
clone_snapshot_command
: clone snapshot [ snapshot_id ]
snapshot_id
: expression
Cloning a snapshot has the following two side effects:
- The snapshots created after the cloned snapshot are deleted. For
example, suppose four snapshots are saved from a process. Cloning the second
snapshot results in the deletion of the third and fourth snapshots.
- The current process is killed and replaced by the clone process. Thus,
if you enter
show process
after cloning a snapshot, you will see that
the process ID of the current process has changed to that of the cloned process.
For example:
(ladebug) show process
>localhost:29013 (/usr/examples/x_list) paused.
(ladebug) clone snapshot
Process has exited
Process 29089 cloned from Snapshot 1.
# 1 saved at 13:27:54 (PID: 29077).
stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
(ladebug) show process
>localhost:29089 (/usr/examples/x_list) paused.
11.3 The show snapshot Command
Use the show snapshot *
and show snapshot all
commands to
display all the snapshots that have been saved from the current process. Use
show snapshot snapshot_id_list
to display the snapshots
specified. If no snapshots are specified, the most-recently saved existing
snapshot is displayed.
show_snapshot_command
: show snapshot [ snapshot_id_list ]
snapshot_id_list
: snapshot_id ,...
| all
| *
snapshot_id
: expression
For example:
(ladebug) show snapshot all
# 1 saved at 13:27:54 (PID: 29077).
stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
11.4 The delete snapshot Command
Use the delete snapshot *
and delete snapshot all
commands to
delete all the snapshots that have been saved from the current process. Use
delete snapshot snapshot_id_list
to delete the specified
snapshots. If no snapshots are specified, the most-recently saved existing
snapshot is deleted.
delete_snapshot_command
: delete snapshot [ snapshot_id_list ]
snapshot_id_list
: snapshot_id ,...
| all
| *
snapshot_id
: expression
For example:
(ladebug) show snapshot all
# 1 saved at 13:27:54 (PID: 29077).
stopped at [int main(void):182 0x1200023f8]
182 List<Node> nodeList;
(ladebug) delete snapshot
(ladebug) show snapshot all
No snapshots have been saved.
11.5 Snapshot Limitations
Snapshots have the following limitations:
- Snapshots are implemented by causing the process to fork. The state
saved in a snapshot does not include I/O and forks. In other words, when you clone
a snapshot, the I/O that has been done since the snapshot was saved is not
undone; likewise, the child processes that have been spawned since the snapshot
was saved are not killed.
- The
save snapshot
command saves the state of the
current process only. If you are doing multiprocess debugging, you might want
to save a snapshot for each process.
- Snapshots on a multithreaded process are not supported.
- Snapshots are not supported for remote, kernel, or core file
debugging.
Chapter 12Debugging Optimized Code
This chapter provides insight into how highly optimizing compilers and
the debugger deal with the consequences of key optimizations
and how that may influence debugging of your program.
This chapter contains the following sections:
Highly optimizing compilers, such as those supplied with the Tru64 UNIX
operating system, generally
transform a program in many ways to make your program run as quickly
as possible. Expressions may be combined, rearranged, or even
eliminated if it is determined they are not needed; expressions and even
complete statements may be moved out of loops; the order of statements
may be changed; calls to functions may be replaced with a copy of the
called function (inlining), the instructions for a single statement
may be interspersed with instructions for other statements both before
and after, and so on. All of these transformations greatly complicate
the ability of a debugger to display information about the state and
progress of your program or to control its execution. The
compilers and the debugger try to avoid presenting erroneous, misleading, or
incomplete information when debugging optimized code, but this not
always possible. For these reasons, it is almost always easier to
debug an unoptimized version of your program.
Why would you ever try to debug an optimized version? The most likely
reason is that the program appears to work correctly when unoptimized
but somehow fails when optimized. As a result, you may have little
choice but to try to isolate the problem using the optimized program.
The most common reason that a program apparently works correctly when
unoptimized but fails when optimized is this:
Your program performs some action whose behavior is undefined or
implementation dependent, and the optimized version is different
from the unoptimized version when performing this action.
For example, your program might read and depend on the value of a
variable that was not assigned a value. When executed in
unoptimized form, the value that happens to be in that variable might
accidently result in the desired behavior. But when optimized, the
variable might have some other value that leads to different behavior.
As another example, sometimes your program may be subtly dependent on
the exact order in which operations are performedand optimization
can result in a different order. There are many other examples that
are beyond the scope of this discussion.
It is also possible that there is a bug in the compiler. While it
does happen, experience with the compilers supplied with the operating
system indicates that this is rare.
In any case, to determine the cause or nature of the problem requires
debugging using the optimized version. Then you can determine how
best to resolve the problem. (Of course, you could also choose to
reduce the level of optimization, possibly to none, to obtain the
desired behavior, but that may not result in acceptable performance.)
Preparing a program for debugging with
optimization involves compiling that program using the -g3
option.
All other preparation is unchanged.
The -g3
option differs from the -g
option in
that it does not affect the optimization level. That is, -g
(equivalent to -g2
)
sets the optimization level to zero (that is, -O0
), even overriding
an explicit optimization setting; -g3
leaves the optimization
level unchanged. See the reference pages for the respective compilers for further
details.
A variable is said to have split lifetimes if the set of fetches and
stores of the variable can be partitioned such that none of the values
stored in one subset are ever fetched in another subset. When such a
partition exists, the variable can be split into several independent
child variables, each corresponding to a partition. As independent
variables, the child variables can be allocated independently. The
effect is that the original variable can be thought to reside in
different locations at different points in timesometimes in a
register, sometimes in memory, and sometimes nowhere at all. Indeed,
it is even possible for the multiple child variables to be active
simultaneously.
Split lifetime information in the debugging symbol table describes
each of the child variables associated with the main variable, where
it is allocated, and the exact range of addresses over which each
child is valid.
Because assignments may not occur in the same order as in the source
code, the split lifetime information also includes a list of all of
the places where the current value may have been assigned. In general,
this is a list of possibilities, because several execution paths may
converge, bringing together multiple assignment possibilities; the debugger
does not trace the exact execution path that reaches a stopping point,
so it can only report the set of relevant alternatives.
When a variable does not have a value at the current location, the debugger
cannot print a value for it and reports an error as follows:
(ladebug) print L
Info: symbol L is defined but not allocated (optimized away)
Error: no value for symbol L
The first error message line indicates that there is a symbol L,
but that it does not currently have a value. The preceding informational
line distinguishes between two cases:
- The symbol is not allocated at all (was optimized away). Such
a symbol can never have a value.
- The symbol does not have a value at the current location. Such
a symbol has split lifetimes and will have a value at other locations,
just not here.
If a variable is not declared at all, then the error report looks
like the following:
(ladebug) print L
Symbol "L" is not defined.
When a variable has a value, there may also be information concerning
where that variable was assigned:
(ladebug) print j
2
Value assigned at line 5
The value may be assigned from several places:
(ladebug) print k
3
Value assigned at one of these lines: 6, 11
It is possible, though unusual, for the same line to be listed more
than once; this means that there is more than one instruction from
the same line that assigns a value.
The following limitations apply:
- When a variable has more than one value at a location,
the debugger displays only the first one discovered and ignores
the rest.
- The list of lines at which a value was possibly assigned may
not be complete. The lines that are listed are definitely
known to be possible assignments; the absence of a line
usually means it did not assign a value, but unfortunately this is not
definitive.
A major problem with stepping through optimized code by line using the
next
and
step
commands in some debuggers
is that the apparent source program location
"bounces" forward and back, forward and back, with the same line often
appearing again and again. In large part this bouncing is due to a
compiler optimization called code scheduling, in which instructions
that arise from the same source line are scheduled, that is, reordered
and intermixed with other instructions, for better execution
performance. For example, in sample programs from a prominent
benchmark suite, the average number of instructions in sequence that
share the same line number is typically between 2 and 3and typically
50 to 70 percent of those sequences consist of just 1 instruction.
Semantic stepping causes the program to execute up to, but not
including, an instruction that causes a semantic effect, as well as
being in a different line. Instructions that cause semantic effects
are instructions that:
- Assign a value to a user variable
- Make a control flow decision
- Make or return from a routine call
The visible effect of Ladebug's support of semantic stepping is that the
step
and
next
commands generally make normal progress through your
program. The effects of optimization cannot be hidden entirely,
however; there will be some occasional stepping backward as well as
forward due to code reordering, and some lines and statements will not
appear because they were optimized away, but the result will be
generally more usable.
In addition to the blurring of line number boundaries as described in
the discussion of semantic stepping, a similar blurring occurs at the
beginning and end of an inner scope boundary. That is, one
instruction may be related to the beginning of a new scope (and line)
in which a new variable is declared and becomes visible, while the next
instruction may revert to a source location (line) before the scope
begins.
Debugging optimized code information in the symbol table includes a
detailed description of the possibly multiple disjoint instruction
ranges that belong to or make up a scope. This helps assure that
variable lookups find the right symbols at the current location.
You are not likely to directly perceive the effects and benefits of
this support; just know that it is part of obtaining correct
information in the presence of optimization.
If you enter the
where
command and the
resulting stack seems to be missing routines, you may be seeing the result
of a compiler optimization called tail calls. That optimization works
as follows:
If a procedure MIDDLE calls a procedure INNER just before returning and
certain conditions are met, then MIDDLE might simply jump to INNER, instead
of saving its own context on the stack first. In this case, INNER will
eventually return directly to MIDDLE's caller, OUTER, and there will be no
record of MIDDLE's existence on the stack.
Then if you stop the application in INNER and enter the where
command,
you will see INNER and OUTER, but not MIDDLE.
Because this transformation can occur more than once, it is possible for
several intermediate calls to appear missing from the context stack. The following
example shows one instance of this transformation:
(ladebug) stop in middle_routine
[#1: stop in int middle_routine(int) ]
(ladebug) run
[1] stopped at [int middle_routine(int):10 0x120001154]
10 return inner_routine(a);
(ladebug) list $curline - 5: 10
5 return 1;
6 }
7
8 int middle_routine(int a)
9 {
> 10 return inner_routine(a);
11 }
12
13 int main(int argc, char* argv[])
14 {
(ladebug) where 2
>0 0x120001154 in middle_routine(...) "c_tailcall.c":10
#1 0x12000117c in main(...) "c_tailcall.c":15
(ladebug) step
stopped at [int inner_routine(int):5 0x120001140]
5 return 1;
(ladebug) list $curline - 5: 10
1 #include <stdio.h>
2
3 int inner_routine(int a)
4 {
> 5 return 1;
6 }
7
8 int middle_routine(int a)
9 {
10 return inner_routine(a);
(ladebug) where 2
>0 0x120001140 in inner_routine(...) "c_tailcall.c":5
#1 0x12000117c in main(...) "c_tailcall.c":15
The conditions that permit this optimization include but are not restricted to the following:
- If MIDDLE returns a value, then INNER produces the value to be returned.
- No stack-allocated variables in MIDDLE are used during the execution of INNER.
- MIDDLE does not establish any exception handlers.
See your compiler documentation for more information.
12.7 Alpha Linux Features
If you are debugging optimized code under the Alpha Linux operating system,
the following information applies:
- The debugger for the Alpha Linux operating system provides several features
that make it easier to debug a program that has been compiled with
optimization. These features are described in this chapter.
-
Support for these features is limited to programs compiled using
certain compilers. These include Compaq Fortran 90, Compaq C, and Compaq C++.
-
Taking advantage of these features requires compiling your
program as described in this chapter. The debugger automatically takes advantage of
specialized information that is included in the resulting executable
program.
-
No Ladebug commands control the debugger behavior when
debugging optimized code. Rather, existing commands sometimes behave
somewhat differently than you would see in the absence of
optimization.
When debugging optimized code, you must use some Ladebug commands
with caution or avoid them completely.
Use with Caution
down, dump, func, return, where, pop, up
These commands generally depend on a
distinct call frame being on the execution stack for each
called function. However, inlining can merge a called
routine into the caller, resulting in
one frame instead of the two (or more) that might be
expected. You can use these commands, but be careful
to make sure that you end up in the frame you intend
after each use and do not be misled.
next, nexti
For a call that is inlined, the next
and nexti
commands will appear to step into the called function
instead of stepping over it.
Avoid Completely
assign
It is generally not possible to reliably assign to a
variable before the value of the variable has been
used.
goto
It is generally not possible to reliably determine
where the first instruction of a line begins or to
avoid repeating instructions from the destination line
that may have been executed already.
if
logical filter in breakpoint commands
The condition expression may not have a value at all
in some places where the expression needs to be
evaluated. Worse yet, the debugger sometimes attempts to
cache the address of a variable, which does not
correctly support split variables.
watch variable
The debugger does not support watching a split
variable. This command will most likely fail because the debugger
cannot watch a variable (child or otherwise) that is allocated in a
register; even if it does appear to
succeed, the debugger will be watching the location of just
one child, even when that location is not relevant.
operator &
It is generally not possible to reliably determine
whether a variable has only one lifetime and thus a unique
address.
This chapter contains sections describing the
limitations on support for the following languages:
13.1 Limitations on Support for Compaq C++
The debugger interprets C++ names and expressions using the language rules described
in The Annotated C++ Reference Manual (Ellis and Stroustrup, 1990,
Addison-Wesley). C++ is a distinct language, rather than a superset of C. Where
the semantics of C and C++ differ, the debugger provides the interpretation
appropriate for the language of the program being debugged.
To be more useful, the debugger relaxes some standard C++ name visibility rules.
For example, you can reference public, protected, and private class members as well as
public ones.
The following limitations apply to debugging a C++ program:
- If a program is not compiled with the
-g
flag, do not set a
breakpoint on an inline
member function; it may confuse the debugger.
- When you use the debugger to display virtual and inherited class
information, the debugger does not support pointers to members of a class.
- The debugger does not support calling the C++ constructs
new
and
delete
. As alternatives, use the
malloc()
and
free()
routines from C.
- Sometimes the debugger does not see class type names with internal linkage, and it issues an error message
stating that the name is overloaded.
- You cannot select a version of an overloaded function that has a type
signature containing ellipsis points (...).
- Pointers to functions with type signatures that contain parameter list or
ellipsis arguments are not supported.
Limitations for debugging templates include the following:
- You cannot specify a template by name in a debugger command. You must use
the name of an instantiation of the template.
- Setting a breakpoint at a line number that is inside a template function
will not necessarily stop at all instantiations of the function within the
given file, but only at a randomly chosen few.
13.2 Limitations on Support for Compaq
Fortran
The debugger and the Tru64 UNIX operating system support the Compaq Fortran language
with certain limitations, which are described in the following sections.
Be aware of the following limitations when you debug a Fortran
program:
- The debugger does not allow setting a breakpoint on a program routine named MAIN.
- Substring notation is not supported.
The following limitations apply only to Compaq Fortran 90:
- Compaq Fortran 90 array constructors, structure constructors, adjustable
arrays, and vector subscripts are not supported.
- Compaq Fortran 90 user-defined (derived) operators are not supported.
- The debugger does not handle variables of 16-bit character data types.
13.2.1 Limitations on Procedure Invocations
Following are the limitations on Compaq Fortran procedure invocations:
- The debugger does not support invocations of user-defined procedures unless
they have been compiled with debug information.
- The debugger does not support complex or real*16 procedure return values.
- The debugger does not support real*16 or complex*32 procedure arguments.
- The debugger supports only the following Fortran intrinsic procedures:
- The mathematical functions (for example, ACOS, ACOSD, SIN, SQRT, and so
on)
- The kind type functions (KIND, SELECTED_INT_KIND, SELECTED_REAL_KIND)
For more information, see the Compaq Fortran Language Reference Manual
section that discusses categories of intrinsic functions.
13.3 Limitations on Support for Compaq
Ada
The debugger and the Tru64 UNIX operating system support the Compaq Ada language with
certain limitations, which are described in the following sections.
13.3.1 Limitations on Expressions
Expressions in Ladebug commands use C source language syntax for operators and
expressions. Data is printed as the equivalent C data type.
The following table shows Ada expressions and Ladebug equivalents.
Ada Expression |
Debugger Equivalent |
Binary operations and unary operations |
Only integer, floating, and Boolean expressions are easily expressed. |
a+b,-,* | a+b,-,* |
a/b | a/b |
a = b /= < <= > >= | a = = b != < <= > >= |
a and b | a&&b |
a or b | a | | b |
a rem b | a%b |
not(a=b) | !(a==b) |
-a | -a |
Qualified expressions | None. There is no easy way of evaluating
subtype bounds. |
Type conversions |
Only simple numeric conversions are supported, and
the bounds checking cannot be done. Furthermore,
float -> integer truncates rather than rounds.
integer -> float
(ladebug) print (float) (2147483647)
2147483648.0
(ladebug) print (double) (2147483647)
2147483647.0 |
Attributes | None, but if E is an enumeration type with default
representations for the values, then:
E'PRED(X) is the same as x-1.
E'SUCC(X) is the same as x+1 |
p.all | *p (pointer reference) |
p.m |
p -> m (member of an "access record" type) |
13.3.2 Limitations on Data Types
This section lists the limitation notes by data type. For more information on
these types, with examples, see the Developing Ada Programs on Tru64 UNIX
Systems manual. Also see the Compaq Ada release notes for
detailed information on debugging.
All Types
The debugger, unlike the Ada language, allows out-of-bounds assignments to be
performed.
Integer Types
If integer types of different sizes are mixed (for example, byte-integer and
word-integer), the one with the smaller size is converted to the larger size.
Floating-Point Types
If integer and floating-point types are mixed in an expression, the debugger
converts the integer type to a floating-point type.
The debugger displays floating-point values that are exact integers in integer
literal format.
Fixed-Point Types
The debugger displays fixed-point values as real-type literals or as structures.
The structure contains values for the sign and the mantissa. To display the
structure's value, multiply the sign and mantissa values. For example:
(ladebug) list 5
5 procedure a_showFixedPointType is
6
7 type FixedPoint is delta 0.1 range -3.0 .. 9.0;
8 myVar : FixedPoint := 1.0;
9
10 begin
> 11 myVar := myVar + 1.0;
12 end;
(ladebug) print myVar
packed object {
fixed_point, small = 0.625E-1 * mantissa = 16;
}
(ladebug) print 6.25E-02 * 16
1.0
Enumeration Types
The debugger displays enumeration values as the actual enumeral or its
position.
You must manually convert enumeration values to 'pos
values before
you can use them as array indices.
Array Types
The debugger displays string array values in horizontal ASCII format,
enclosed in quotation ("x") marks. A single component (character) is displayed
within single quotation ('x') marks.
The debugger allows you to assign a component value to a single component; you
cannot assign using an entire array or array aggregate.
Arrays whose components are neither a single bit nor a multiple of bytes are
described to the debugger as structures; a print
command displays
only the first component of such arrays.
Records
The debugger cannot display record components whose offsets from the start of
the record are not known at compile time.
For variant records, however, the debugger can display the entire record
object that has been declared with the default variant value. The debugger
allows you to print or assign a value to a component of a record variant
that is not active.
Access Types
The debugger does not support allocators, so you cannot create new access
objects with the debugger. When you specify the name of an access object, the
debugger displays the memory location of the object it designates. You can
examine the memory location value.
13.3.3 Limitations on Tasking Programs
When you debug Ada tasking programs, you use the debugger and the DEC Ada
ada_debug
routine.
13.4 Limitations on Support for Compaq
COBOL
This section describes the limitations on support for Compaq COBOL.
13.4.1 Limitations on Assignment
The following limitations apply to assignment in COBOL debugging:
The debugger allows assignment of the values 1.23 or 8765.22 to firstItem
,
but does not allow assignment of the value 1.2 to firstItem
. The following
debugger commands are supported, because the quantities on both sides of the
assignment operator (=) have the same scale:
(ladebug) print firstItem, secondItem
2.46 1234.56
(ladebug) assign firstItem = 1.23
(ladebug) assign secondItem = 8765.22
(ladebug) print firstItem, secondItem
1.23 8765.22
(ladebug) assign firstItem = secondItem
(ladebug) print firstItem, secondItem
8765.22 8765.22
The following debugger commands are not supported, because the quantities
involved are of different scales:
(ladebug) assign firstItem = 1.2
This item may only be assigned values of equivalent scale (-2)
(ladebug) assign thirdItem = firstItem
This item may only be assigned values of equivalent scale (-5)
- You cannot assign a value of greater precision to an item. Given the
declarations, the following debugger command is not supported, because
firstItem
has greater precision than secondItem
:
(ladebug) assign secondItem = firstItem
This item may only be assigned values with a maximum precision of (6)
- With numeric edited items, the precisions of the quantities on both sides of
the assignment must be the same.
- A numeric edited item cannot be assigned to other numeric items.
13.4.2 Other Limitations
Other limitations when you debug COBOL programs include the following:
- Qualification involving more than one intervening level produces a debugger
error if the intervening level is an OCCURS item.
- Some debugger command usage is affected by COBOL language syntax when
execution is stopped within a COBOL procedure. One effect is that expressions
typed on the Ladebug command line must include spaces around arithmetic
operators, such as plus (+) and minus (-).
Another effect of COBOL language syntax is in the debugger memory-examine
command. For example, to look at the next 10 program instructions, you would
normally use:
(ladebug) &coboldata/10i
Bad or unimplemented printout mode for examine command.
Using X instead.
0x120002a90: 23bd5c8027bb2000
This debugger command says "from the given program location" (signified by
the address of coboldata
), examine the next 10 program locations (10 being
the count) in instruction mode (signified by the i
).
When debugging COBOL programs, you need to enter this command as follows:
(ladebug) &coboldata/10 i
coboldata(void): c_coboldata.cob
[line 2, 0x120002a90] ldah gp, 8192(r27)
[line 2, 0x120002a94] lda gp, 23680(gp)
[line 2, 0x120002a98] lda sp, -144(sp)
[line 2, 0x120002a9c] stq r26, 48(sp)
[line 2, 0x120002aa0] stq r9, 56(sp)
[line 2, 0x120002aa4] ldq r9, -32720(gp)
[line 2, 0x120002aa8] ldah r2, -1(gp)
[line 2, 0x120002aac] ldq r27, -32488(gp)
[line 2, 0x120002ab0] ldq r16, -32736(gp)
[line 58, 0x120002ab4] lda r9, -21(r9)
Add a space between the count (10) and the mode indicator (i).
- Reference modification is not supported.
- The OF qualifier keyword is supported, but the IN keyword is not supported.
- Tables (OCCURS items) with variable upper bounds are not supported.
- Breakpoints on labels are not supported.
- Subscripting is supported, but only for tables of one dimension.
- Stopping program execution at a program label (paragraph or section name)
is not supported.
- The debugger does not correctly evaluate all COBOL condition expressions.
- The debugger supports only simple arithmetic operations in COBOL. It does
not support full expression evaluation, including exponentiation, reference
modification, and arithmetic operations with operands of different scales.
Part III
Advanced Topics
Part III provides information to make advanced use of the debugger by
expanding on topics presented in Part II.
It is intended to be used as a reference and therefore contains a wide variation
in the level of information provided in each section.
This chapter describes how you can modify your program to wait for the
debugger and to print a stack trace.
To modify your program to wait for the debugger, complete the following steps:
- Add some code similar to the following :
void loopForDebugger()
{
if (!getenv("loopForDebugger")) return;
static volatile int debuggerPresent = 0;
while (!debuggerPresent);
}
- Call this function at some convenient point.
- Use the debugger's
attach
capability to bring
the process under control.
When you have the process under debugger control, you can assign
a nonzero value to debuggerPresent
, and continue your program. For example:
% setenv loopForDebugger ""
% a.out arg1 arg2 &
% ladebug -pid 1709 a.out
^C
(ladebug) assign debuggerPresent = 1
...
(ladebug) cont
This section contains code you can use to modify your program to print a
stack trace.
You can also call the
debugBreak
function to start the
debugger at some specified point after your program starts executing.
For more information about debugBreak
, see the
Ladebug
Web site FAQ.
/*% cc -g traceback.c -lexc
**% a.out
*/
struct exception_info;
/* To suppress compilation warnings from sym.h */
#include <demangle.h>
#include <excpt.h>
#include <setjmp.h>
#include <signal.h>
#include <sym.h>
#include <unistd.h>
static void printHeader(void)
{
printf("Diagnostic stack trace ...\n");
}
static void printTrailer(void)
{
printf("end of diagnostic stack trace.\n");
}
#define RETURNADDRREG (26)
#define FAULTING_ADDRESS sc_traparg_a0
extern unsigned long _procedure_string_table;
extern unsigned long _procedure_table_size;
extern unsigned long _procedure_table;
extern unsigned long _ftext; /* Start address of the program text.*/
extern unsigned long _etext; /* First address above the program text.*/
/* Turn a PC into the name of the routine containing that PC.
*/
char* programCounterToName(
unsigned long programCounter);
/* Unwind the stack one level.
*/
void unwindOneLevel(
struct sigcontext* unwindSignalContext,
void * runtimeProcedurePtr);
void printFrame(
void * rpdp,
unsigned long programCounter)
{
char* procedureName;
/* Get the procedure name.
*/
procedureName = programCounterToName(programCounter);
printf("%p %s\n",
(void* ) programCounter, procedureName ? procedureName : "");
}
char* programCounterToName(
unsigned long programCounter)
{
long int procedureTableSize;
char * procedureNameTable;
char * procedureName;
long int currentProcedure;
long int procedure;
pRPDR runTimePDPtr;
int found;
unsigned long int address;
found = 0;
procedureName = 0;
procedureTableSize = (long int)&_procedure_table_size;
procedureNameTable = (char *)&_procedure_string_table;
runTimePDPtr = (pRPDR)&_procedure_table;
/* The table of run time procedure descriptors is ordered by
procedure start address. Search the table (linearly) for the
first procedure with a start address larger than the one for
which we are looking.
*/
for (currentProcedure = 0;
currentProcedure < procedureTableSize;
currentProcedure++) {
/* Because of the way the linker builds the table we need to make
special cases of the first and last entries. The linker uses 0
for the address of the procedure in the first entry, here we
substitute the start address of the text segment. The linker
uses -1 for the address of the procedure in the last entry,
here we substitute the end address of the text segment. For all
other entries the procedure address is obtained from the table.*/
if (currentProcedure == 0) /* first entry */
address = (unsigned long int)&_ftext;
else if (currentProcedure == procedureTableSize - 1) /* last entry */
address = (unsigned long int)&_etext;
else /* other entries */
address = runTimePDPtr[currentProcedure].adr;
if (address > programCounter) {
if (currentProcedure != 0) {
/* the PC is in this image */
found = 1;
procedure = currentProcedure - 1; /* the PC is in preceeding procedure */
break; /* stop searching */
} else {
/* the PC is outside this image (at a lower address) */
break; /* stop searching */ }
}
}
if (found) {
/* the PC is inside this image */
procedureName = &procedureNameTable[runTimePDPtr[procedure].irpss];
} else {
/* the PC is outside this image, probably in a different shared object */
procedureName = 0;
}
return procedureName;
}
void unwindOneLevel(
struct sigcontext *unwindSignalContext,
void * runtimeProcedurePtr)
{
unwind(unwindSignalContext, (pdsc_crd *)runtimeProcedurePtr);
}
/* Print a stack trace.
*/
void printStackWkr(struct sigcontext *signalContextPtr)
{
struct sigcontext unwindSignalContext;
void *runTimeProcedurePtr;
unsigned long int stackpointer = 0;
unsigned long int programCounter = 0;
unsigned long int returnAddress = 0;
unsigned long int faultingAddress = 0;
printHeader();
/* Set the initial context for the unwind.
*/
unwindSignalContext = *signalContextPtr;
/* Pick out the return address and program counter.
*/
returnAddress = unwindSignalContext.sc_regs[RETURNADDRREG];
programCounter = unwindSignalContext.sc_pc;
/* This is the address that caused the fault when we tried to access
it.
*/
faultingAddress = signalContextPtr->FAULTING_ADDRESS;
/* Special cases for bogus program counter values. If the program
counter is zero or the fault occurred when we were trying to
fetch an instruction (because the program counter itself was bad)
then we cannot unwind the stack.
*/
if (programCounter == 0) {
printf("PC is zero - stack trace not available.\n");
} else if (programCounter == faultingAddress) {
printf("bad PC (%p) - stack trace not available.\n",
faultingAddress);
} else {
unsigned int sameSpCount = 0;
/* Loop through all the stack frames.
*/
while ((returnAddress != 0) && (programCounter != 0)) {
/* Get the run time procedure descriptor for this frame.
*/
runTimeProcedurePtr = find_rpd(programCounter);
/* Print a frame.
*/
printFrame(runTimeProcedurePtr, programCounter);
/* Unwind one level.
*/
unwindOneLevel(&unwindSignalContext, runTimeProcedurePtr);
returnAddress = unwindSignalContext.sc_regs[RETURNADDRREG];
programCounter = unwindSignalContext.sc_pc;
if (unwindSignalContext.sc_sp <= stackpointer) {
sameSpCount++;
if (sameSpCount == 10) break;
} else {
sameSpCount = 0;
stackpointer = unwindSignalContext.sc_sp;
}
}
}
printTrailer();
}
/* Discard one stack frame by silently unwinding.
*/
long int discardOneLevel(
long int programCounter,
struct sigcontext *signalContextPtr)
{
void *runTimeProcedurePtr;
runTimeProcedurePtr = find_rpd(programCounter);
unwindOneLevel(signalContextPtr, runTimeProcedurePtr);
return signalContextPtr->sc_pc;
}
void printStack(int levelsToDiscard)
{
long int programCounter;
void *runTimeProcedurePtr;
jmp_buf context;
struct sigcontext *signalContextPtr;
/* Capture the current context.
*/
setjmp(context);
signalContextPtr = (struct sigcontext *)context;
programCounter = signalContextPtr->sc_pc;
/* Discard the frame for this routine.
*/
programCounter = discardOneLevel(programCounter, signalContextPtr);
/* Discard any other frames requested by our caller.
*/
if (levelsToDiscard > 0) {
int levels;
for (levels = 0; levels < levelsToDiscard; levels++) {
programCounter = discardOneLevel(programCounter, signalContextPtr);
}
}
/* Now that we have the right context, print the stack trace.
*/
printStackWkr(signalContextPtr);
}
/* Example of usage follows. */
void innermost()
{
printStack(0);
}
void middle()
{
innermost();
}
int main()
{
middle();
return 0;
}
This chapter is divided into the following sections:
After the debugger assembles complete lines of input from the characters it
reads from stdin
or
from the file specified in the source
command
(as described in Part II), each line is
then converted into a sequence of lexical elements known as
lexemes. For example, print(a++);
is converted into the
following lexemes:
print
(
a
++
)
;
The sequence of lexemes is then recognized as Ladebug commands containing
language-specific expressions by a process known as parsing. The recognition
is based on a set of rules known as a grammar. The
debugger uses a single grammar for commands regardless of the current program's
language, but it has language-specific subgrammars for some of the pieces of commands,
such as names, expressions, and so on.
The debugger starts processing each line according to the rules of the LKEYWORD
lexical state. It recognizes the longest lexeme it can, according to these rules.
After recognizing the lexeme, it may stay in the same state, or it may
change to a different lexical state with different rules.
The debugger moves between the following lexical states as it
recognizes the lexemes:
Lexical State |
Description |
LBPT |
Breakpoint commands have lexemes that change the lexical state to LBPT. |
LEXPORT |
The command export changes the lexical state to
LEXPORT. This state recognizes an evironment variable identifier. |
LFILE |
Commands that require file names have lexemes that change the
lexical state to LFILE so that things like mysrc/foo.txt are
recognized as
a file name, and not as a variable mysrc being divided by a
structure data member foo.txt . |
LFINT |
Commands that require either a file name or a process ID have lexemes to
change the lexical state to LFINT. |
LKEYWORD |
All but one of the Ladebug commands begin with one or more
keywords. The exception is the examine command.
The debugger recognizes the '{' and ';' lexemes that precede a
command, and uses those to reset to the LKEYWORD state for the beginning of
the next command. |
LLINE |
Commands that require arbitrary character input have lexemes that
change the lexical state to LLINE. |
LNORM |
Language expressions involve language-specific lexemes. The lexemes
that precede an expression change the lexical state to LNORM, and then LNORM
recognizes the language-specific lexemes. |
LSETENV |
The command setenv changes the lexical state to
LSETENV. This state recognizes an evironment variable identifier.
|
LSIGNAL |
Commands that require signal names have lexemes that
change the lexical state to LSIGNAL. |
LWORD |
Commands that require shell words have lexemes that
change the lexical state to LWORD. |
The rules that each lexical state uses are described in the following sections,
in a format known as a lex regular expression. Rather than repeating some of
descriptions, a set of common subrules is described first.
After the common subrules, we describe the initial state, the rules, and for each
recognized token, the next lexical state.
All languages have a concept of an identifier, composed of letters, digits,
and other special characters. The debugger also uses keywords composed of letters;
therefore, rules are required to determine which identifiers are actually
Ladebug keywords.
All Ladebug commands, except
examine
, begin with
leading keywords. Because the
examine
command
begins with an expression, all identifiers must be recognized as such
from both the LKEYWORD state
that starts commands and the LNORM state
that the debugger uses for processing expressions.
Some Ladebug commands have keywords embedded in them
following expressions, and the ends of expressions are hard to recognize.
You can use identifiers that have the same spelling as an embedded keyword
simply by enclosing the whole expression in parentheses (()
).
For more information on using keywords within commands, see
Section 4.4.4.
Furthermore, the C and C++ grammars need to know whether an identifier is a
typedef
or struct/class
identifier. The debugger currently
makes the determination at the time the
whole command is parsed, rather than deferring the determination to when the
expression itself is being evaluated. This can result in a misidentification of the
identifier.
When in the following four lexical states, the debugger can recognize
identifiers:
- LKEYWORD, LNORM, LBPT
Language |
Regular Expression |
C, C++, Fortran, Ada |
{LT}({LT}|{DG})* |
COBOL | CobolWord |
The state is changed to LNORM to process the rest of the expression.
- LSIGNAL
Language |
Regular Expression |
All | {LT}({LT}|{DG})* |
The state is left as LSIGNAL to process the next signal.
If your operating system supports internationalization (I18N),
{LT}
equates to {LTI18N}
.
The complete set of embedded keywords follows:
NOTE: THREAD
is both a leading and an embedded keyword.
Leading keywords are recognized only at the beginning of commands. You do
not need to use parentheses (()
) to use them as a normal identifier,
unless they occur at the start of an examine
command.
Leading keywords may differ between languages. The complete set follows:
Some identifiers are recognized as reserved words, regardless of whether they
are inside parentheses (()
).
The reserved words may differ between languages. The complete set follows:
Lexeme |
Representation (Some May Be Language Specific) |
Initial Lexical State |
Changed Lexical |
Language Specific? |
AND |
AND |
LNORM | LNORM |
COBOL |
MOD |
MOD |
LNORM | LNORM |
COBOL |
NOT |
NOT |
LNORM | LNORM |
COBOL |
OR |
OR |
LNORM | LNORM |
COBOL |
BYCONTENT |
(BY[ \t]+)?CONTENT |
LNORM | LNORM |
COBOL |
BYDESCRIPTOR |
(BY[ \t]+)?DESCRIPTOR |
LNORM | LNORM |
COBOL |
BYREFERENCE |
(BY[ \t]+)?REFERENCE |
LNORM | LNORM |
COBOL |
BYVALUE |
(BY[ \t]+)?VALUE |
LNORM | LNORM |
COBOL |
CHAR |
char |
LNORM | LNORM |
C, C++ |
CLASS |
class |
LNORM | LNORM |
C++ |
CONST |
const |
LNORM | LNORM |
C, C++ |
DELETE |
delete |
LNORM | LNORM |
C++ |
DOUBLE |
double |
LNORM | LNORM |
C, C++ |
ENUM |
enum |
LNORM | LNORM |
C, C++ |
FLOAT |
float |
LNORM | LNORM |
C, C++ |
GIVING |
GIVING |
LNORM | LNORM |
COBOL |
INT |
int |
LNORM | LNORM |
C, C++ |
LONG |
long |
LNORM | LNORM |
C, C++ |
NEW |
new |
LNORM | LNORM |
C++ |
OF |
OF |
LNORM | LNORM |
COBOL |
OPERATOR |
operator |
LNORM | LNORM |
C++ |
REFERENCEOF |
REFERENCE([ \t]+"OF")? |
LNORM | LNORM |
COBOL |
SHORT |
short |
LNORM | LNORM |
C, C++ |
SIGNED |
signed |
LNORM | LNORM |
C, C++ |
SIGNNEG |
(IS[ \t]+)?"NEGATIVE" (IS[ \t]+)?"NOT"[ \t]+"POSITIVE" |
LNORM | LNORM |
COBOL |
SIGNNOTZERO |
(IS[ \t]+)?"NOT"[ \t]+"ZERO" |
LNORM | LNORM |
COBOL |
SIGNPOS |
(IS[ \t]+)?"POSITIVE" (IS[ \t]+)?"NOT"[ \t]+"NEGATIVE" |
LNORM | LNORM |
COBOL |
SIGNZERO |
(IS[ \t]+)?"ZERO" |
LNORM | LNORM |
COBOL |
SIZEOF |
sizeof |
LNORM | LNORM |
C, C++, Fortran |
STRUCT |
struct |
LNORM | LNORM |
C, C++ |
UNION |
union |
LNORM | LNORM |
C, C++ |
UNSIGNED |
unsigned |
LNORM | LNORM |
C, C++ |
USING |
USING |
LNORM | LNORM |
COBOL |
VOID |
void |
LNORM | LNORM |
C, C++ |
VOLATILE |
volatile |
LNORM | LNORM |
C, C++ |
Because the debugger supports multiple languages, some of the rules must be
language specific. To distinguish between the characters used for a particular
language to represent a lexeme and the lexeme itself, the debugger names the lexemes,
rather than using any one language's representation. For example, the lexeme
GE corresponds to Fortran's '.GE.', to C's '>=', and
to COBOL's 'IS NOT LESS THAN'.
Some lexemes have the same representation in all languages, especially those
that form part of the Ladebug commands apart from the language-specific
expressions.
The following tables list common elements of lexemes.
Concept | Rule |
Representation | Description |
Decimal digit |
DG |
[0-9] |
One character from '0'..'9'. |
Octal digit |
OC |
[0-7] |
One character from '0'..'7'. |
Hexadecimal digit |
HX |
[0-9a-fA-F] |
Any of the characters '0'..'9' and any of the
letters 'A'..'F' and 'a'..'f'. |
Single letter |
LT |
[A-Za-z_$] |
Any of the characters 'A'..'Z', 'a'..'z', and the underscore (_) and dollar sign ($)
characters. |
Single letter
from the International Character Set |
LT18N |
[A-Za-z_$\200-\377] |
Any of the characters 'A'..'Z', 'a'..'z', the underscore (_) and dollar sign ($)
characters, and any character in the top half of the 8-bit character set.
|
Shell 'word' |
WD |
[^ \t;\n<>'"] |
Any character except space, tab, semicolon (;), linefeed, less than (<),
greater than (>), and quotes (' or ").
|
File name |
FL |
[^ \t\n\}\;\<>] |
Any character except space, tab, semicolon (;), linefeed, right brace (}), less than (<),
greater than (>), and tick (`).
|
Optional exponent |
Exponent |
[eE][+-]?{DG}+ |
Numbers often allow an optional exponent. It is represented as an 'e' or
'E' followed by an optional plus (+) or minus (-), and then one or more decimal digits.
|
Whitespace |
Whitespace |
[ \t]+ |
Whitespace is often used to separate two lexemes that would
otherwise be misconstrued as a single lexeme. For example, stop in is two
keywords, but stopin is an identifier. Apart from this separating property,
Whitespace is usually ignored. Whitespace is a sequence of one or more tabs or
spaces.
|
String literal |
stringChar |
([^"\\\n]|([\\]({simpleEscape}|
{octalEscape}|{hexEscape})))
|
Any character except the terminating quote character ("), or
a new line (\n). If the character is a backslash (\), it is followed by an escaped
sequence of characters.
|
Character literal |
charChar |
([^'\\\n]|([\\]({simpleEscape}|
{octalEscape}|{hexEscape})))
|
Any character except the terminating quote (') character, or
a new line (\n). If the character is a backslash (\), it is followed by an escaped
sequence of characters.
|
Environment variable identifier |
EID |
[^ \t\n<>;='"&\|] |
Any character except space, tab, linefeed, less-than (<),
greater-than (>), semicolon (;), equal sign (=),
quotes (' or "),
ampersand (&), backslash (\), and bar (|).
|
Universal character name |
UCN |
\\u{HX}{4}|\\U{HX}{8} |
A universal character name is a backslash (\) followed by either
a lowercase 'u' and 4 hexadecimal digits, or an uppercase 'U' and 8
hexadecimal digits. |
The escaped sequence of characters can be one of following three forms:
Concept | Rule |
Representation | Description |
Simple escape |
simpleEscape |
([A-Za-z'"?#*\\])
|
One of 'A'-'Z' or 'a'-'z'. Some of these have special meanings,
the most common being 'n' for new line and 't' for tab. Can be a quote (' or ")
character that does not finish the literal, a question mark (?), a pound sign (#), an
asterisk (*), or
a backslash (\), which then becomes part of the string literal rather than causing a
further escape sequence.
|
Octal escape |
octalEscape |
(OC{1,3})
|
1 to 3 octal digits,
the combined numeric value of which is the
character that becomes part of the string literal.
|
Hexadecimal escape |
hexEscape |
([xX]HX{1,8})
|
An 'x' or an 'X' followed by 1 to 8 hexadecimal digits,
the combined numeric value of which is the character that becomes part of the string literal.
|
In all lexical states, unescaped new lines produce the NEWLINE token and
change the lexical state to be LKEYWORD.
In all lexical states except LLINE, a semicolon also changes the lexical
state to be LKEYWORD.
Initial State |
LKEYWORD, LNORM, LFILE, LLINE, LWORD, LSIGNAL, LBPT |
Regular expression | [\n] |
Lexeme | NEWLINE |
Change to state | LKEYWORD |
This is because SEMICOLON is the command separator.
Initial State |
LKEYWORD, LNORM, LFILE, LSIGNAL, LBPT, LWORD |
Regular expression | ";" |
Lexeme | SEMICOLON |
Change to state | LKEYWORD |
Commands can be nested, and the following transitions support this:
Initial State |
LNORM |
Regular expression | "{" |
Lexeme | LBRACE |
Change to state | LKEYWORD |
Initial State |
LKEYWORD, LNORM, LFILE, LSIGNAL, LBPT |
Regular expression | "}" |
Lexeme | RBRACE |
Change to state | LKEYWORD |
In most lexical states, the spaces, tabs, and escaped new lines are ignored.
In the LLINE state, the spaces and tabs are part of the line, but escaped
new lines are still ignored.
In the LWORD state, the spaces and tabs are ignored, but escaped new lines
are not.
Initial State |
LKEYWORD, LNORM, LFILE, LSIGNAL, LBPT |
Regular expression | [ \t] \\\n |
Lexeme | Ignored |
Change to state | Unchanged |
Initial State |
LLINE |
Regular expression | \\\n |
Lexeme | Ignored |
Change to state | Unchanged |
Initial State |
LWORD |
Regular Expression | [ \t] |
Lexeme | Ignored |
Change to State | Unchanged |
The state stays in LNORM.
Lexeme | Regular Expression |
ANY |
any |
AT |
at |
ATSIGN |
"@" |
CHANGED |
changed |
CHARACTERconstant |
[lL][']{charChar}+['] |
COLON |
":" |
COMMA |
"," |
DOLLAR |
"$" |
DOT |
"." |
GE |
">=" |
GREATER |
">" |
HASH |
unknown |
IF |
if |
IN |
in |
IN_ALL |
in{Whitespace}all{Whitespace} |
INTEGERconstant |
"0"[kK]{HX}+ |
LE |
"<=" |
LESS |
"<" |
LPAREN |
"(" |
POLICY |
policy |
PRIORITY |
priority |
RPAREN |
")" |
READ |
read |
SLASH |
"/" |
STAR |
"*" |
STATE |
state |
STRINGliteral |
["]{stringChar}*["] |
THREAD |
thread |
THREAD_ALL |
thread{Whitespace}all
thread{Whitespace}"*" |
TICK |
"`" |
TO |
to |
WIDECHARACTERconstant |
[lL][']{charChar}+['] |
WIDESTRINGliteral |
[lL]["]{stringChar}*["] |
WITH |
with |
WITHIN |
within |
WRITE |
write |
Files are one or more characters that can appear in a file name.
The state is left as LFILE, so that commands such as use
and
unuse
can have lists of files.
Lexeme | Regular Expression |
FILENAME |
{FL}+ |
The state remains in LKEYWORD.
All characters up to the next new line are assembled into a STRINGliteral.
Once the lexical state has been set to LWORD, it will stay there until a
NEWLINE or a SEMICOLON is found. Both of these cause the lexical state to
become LKEYWORD again.
The individual words recognized can be any of the following, but in each
case, the state stays LWORD:
Lexeme | Regular Expression |
GREATER |
">" |
LESS |
"<" |
GREATERAMPERSAND |
">&" |
ONEGREATER |
"1>" |
TWOGREATER |
"2>" |
STRINGliteral |
[']{charChar}*['] ["]{stringChar}*["] |
STRINGliteral |
{WD}* that does not end in a backslash |
WIDECHARACTERconstant |
[lL][']{charChar}+['] |
WIDESTRINGliteral |
[lL]["]{stringChar}*["] |
The state stays in LSIGNAL.
Lexeme | Regular Expression |
INTEGERconstant |
{DG}+ |
IDENTIFIER |
{LT}({LT}|{DG})* |
Lexeme | Regular Expression |
ENVARID |
{EID} + |
Lexeme |
Representation (Some May Be Language Specific) |
Initial Lexical State |
Changed Lexical State |
Language Specific? |
ALPHASDIGITSI18N |
[0-9A-Za-z_$\200-\377] |
LNORM, LBPT | LNORM |
COBOL |
ALPHASDIGITS
HYPHENI18N |
[0-9A-Za-z_$\200-\377\-] |
LNORM, LBPT | LNORM |
COBOL |
AMPERSAND |
"&" |
LNORM | Unchanged |
C, C++, Fortran, Ada |
AND |
AND |
LNORM | Unchanged |
COBOL |
ANDAND |
"&&" |
LNORM | Unchanged |
C, C++, Ada |
ANDassign |
"&=" |
LNORM | Unchanged |
C, C++ |
ARROW |
"->" |
LNORM | Unchanged |
C, C++, Ada |
ARROWstar |
"->*" |
LNORM | Unchanged |
C++ |
ASSIGNOP |
"=" |
LNORM | Unchanged |
C, C++, Fortran, Ada, COBOL |
BRACKETS |
"[]" |
LNORM | Unchanged |
C, C++, Ada |
CLCL |
"::" |
LNORM | Unchanged |
C++ |
DECR |
"--" |
LNORM | Unchanged |
C, C++, Ada |
DIVassign |
"/=" |
LNORM | Unchanged |
C, C++, Ada |
DOTstar |
".*" |
LNORM | Unchanged |
C++ |
ELLIPSIS |
"..." |
LNORM | Unchanged |
C++ |
EQ |
"=="
".EQ."
(IS[ \t]+)?
("="|("EQUAL"([ \t]+"TO")?)) |
LNORM | Unchanged |
C, C++, Ada, Fortran
Fortran
COBOL
|
ERassign |
"^=" |
LNORM | Unchanged |
C, C++ |
EXPON |
"**" |
LNORM | Unchanged |
COBOL |
GE |
".GE." (IS[ \t]+)? "NOT"[ \t]+ ("<"|("LESS"([ \t]+"THAN")?)) (IS[ \t]+)? (">="|("GREATER"([ \t]+"THAN")?[ \t] +"OR"[ \t]+"EQUAL"([ \t]+"TO")?)) |
LNORM | Unchanged |
Fortran
COBOL
|
GREATER |
".GT." (IS[ \t]+)? (">"|("GREATER"([ \t]+"THAN")?)) |
LNORM | Unchanged |
Fortran
COBOL
|
HAT |
"^" |
LNORM | Unchanged |
C, C++, Ada |
INCR |
"++" |
LNORM | Unchanged |
C, C++, Ada |
LBRACKET |
"[" |
LNORM | Unchanged |
C, C++, Fortran, Ada |
LE |
".LE." (IS[ \t]+)?"NOT"[ \t]+ (">"|("GREATER"([ \t]+"THAN")?)) (IS[ \t]+)? ("<="|("LESS"([ \t]+"THAN")?[ \t]+ "OR"[ \t]+"EQUAL"([ \t]+"TO")?)) |
LNORM | Unchanged |
Fortran
COBOL
|
LESS |
".LT." (IS[ \t]+)? ("<"|("LESS"([ \t]+"THAN")?)) |
LNORM | Unchanged |
Fortran
COBOL
|
LOGAND |
".AND." |
LNORM | Unchanged |
Fortran |
LOGEQV |
".EQV." |
LNORM | Unchanged |
Fortran |
LOGNEQV |
".NEQV." |
LNORM | Unchanged |
Fortran |
LOGNOT |
".NOT." |
LNORM | Unchanged |
Fortran |
LOGOR |
".OR." |
LNORM | Unchanged |
Fortran |
LOGXOR |
".XOR." |
LNORM | Unchanged |
Fortran |
LS |
"<<" |
LNORM | Unchanged |
C, C++, Ada |
LSassign |
"<<=" |
LNORM | Unchanged |
C, C++ |
MINUS |
"-" |
LNORM | Unchanged |
C, C++, Fortran, Ada, COBOL |
MINUSassign |
"-=" |
LNORM | Unchanged |
C, C++ |
MOD |
"%" MOD |
LNORM | Unchanged |
C, C++, Ada, COBOL |
MODassign |
"%=" |
LNORM | Unchanged |
C, C++, Ada |
MULTassign |
"*=" |
LNORM | Unchanged |
C, C++, Ada |
NE |
"!="
".NE."
"/="
(IS[ \t]+)?
"NOT"[ \t]+("="|("EQUAL"([ \t]+"TO")?))
|
LNORM | Unchanged |
C, C++, Ada
Fortran
COBOL
|
NOT |
"!" NOT |
LNORM | Unchanged |
C, C++, Ada,
COBOL |
OPENSLASH |
"(/" |
LNORM | Unchanged |
Fortran |
OR |
"|" OR |
LNORM | Unchanged |
C, C++, Ada, COBOL |
OROR |
"||" |
LNORM | Unchanged |
C, C++, Ada |
ORassign |
"|=" |
LNORM | Unchanged |
C, C++ |
PARENS |
"()" |
LNORM | Unchanged |
C++ |
PERCENT |
"%" |
LNORM | Unchanged |
Fortran |
PLUS |
"+" |
LNORM | Unchanged |
C, C++, Fortran, COBOL |
PLUSassign |
"+=" |
LNORM | Unchanged |
C, C++, Ada |
QUESTION |
"?" |
LNORM | Unchanged |
C, C++, Ada |
RBRACKET |
"]" |
LNORM | Unchanged |
C, C++, Fortran, Ada |
RS |
">>" |
LNORM | Unchanged |
C, C++, Ada |
RSassign |
">>=" |
LNORM | Unchanged |
C, C++ |
SLASHCLOSE |
"/)" |
LNORM | Unchanged |
Fortran |
SLASHSLASH |
"//" |
LNORM | Unchanged |
Fortran |
STARSTAR |
"**" |
LNORM | Unchanged |
Fortran |
TWIDDLE |
"~" |
LNORM | Unchanged |
C, C++, Ada |
If a C++ identifier is followed by a "::", it is assumed to be a class or
namespace identifier.
If a C++ identifier is followed by a "<", complex and dubious checks are
made to try to match a complete template instance specifier.
The lexemes in the following table are specific to C and C++.
The state stays in LNORM.
Lexeme |
Representation |
Language |
ARROW |
"->" |
C, C++
|
INCR |
"++" |
C, C++
|
DECR |
"--" |
C, C++
|
LS |
"<<" |
C, C++
|
RS |
">>" |
C, C++
|
EQ |
"==" |
C, C++
|
NE |
"!=" |
C, C++
|
ANDAND |
"&&" |
C, C++
|
OROR |
"||" |
C, C++
|
MULTassign |
"*=" |
C, C++
|
DIVassign |
"/=" |
C, C++
|
MODassign |
"%=" |
C, C++
|
PLUSassign |
"+=" |
C, C++
|
MINUSassign |
"-=" |
C, C++
|
LSassign |
"<<=" |
C, C++
|
RSassign |
">>=" |
C, C++
|
ANDassign |
"&=" |
C, C++
|
ERassign |
"^=" |
C, C++
|
ORassign |
"|=" |
C, C++
|
PLUS |
"+" |
C, C++
|
MINUS |
"-" |
C, C++
|
MOD |
"%" |
C, C++
|
HAT |
"^" |
C, C++
|
AMPERSAND |
"&" |
C, C++
|
OR |
"|" |
C, C++
|
TWIDDLE |
"~" |
C, C++
|
NOT |
"!" |
C, C++
|
BRACKETS |
"[]" |
C, C++
|
ASSIGNOP |
"=" |
C, C++
|
LBRACKET |
"[" |
C, C++
|
RBRACKET |
"]" |
C, C++
|
QUESTION |
"?" |
C, C++
|
CHAR |
char |
C, C++
|
DOUBLE |
double |
C, C++
|
FLOAT |
float |
C, C++
|
INT |
int |
C, C++
|
LONG |
long |
C, C++
|
SHORT |
short |
C, C++
|
SIGNED |
signed |
C, C++
|
UNSIGNED |
unsigned |
C, C++
|
VOID |
void |
C, C++
|
CONST |
const |
C, C++
|
VOLATILE |
volatile |
C, C++
|
SIZEOF |
sizeof |
C, C++
|
ENUM |
enum |
C, C++
|
STRUCT |
struct |
C, C++
|
UNION |
union |
C, C++
|
INTEGERconstant |
"0"{OC}+
"0"[xX]{HX}+
{DG}+ |
C, C++
|
FLOATINGconstant |
{DG}*"."{DG}*
{DG}*"."{DG}* {Whitespace}?{Exponent}
{DG}+{Whitespace}?{Exponent
} |
C, C++
|
IDENTIFIER, TYPEDEFname |
{LT}|{UCN})({LT}|{UCN}|{DG})* |
C, C++
|
The lexemes in the following table are specific to C++. The state stays in
LNORM.
Lexeme |
Representation |
Language |
OPERATOR |
operator |
C++
|
NEW |
new |
C++
|
DELETE |
delete |
C++
|
CLASS |
class |
C++
|
ELLIPSIS |
"..." |
C++
|
CLCL |
"::" |
C++
|
THIS |
this |
C++
|
DOTstar |
".*" |
C++
|
ARROWstar |
"->*" |
C++
|
PARENS |
"()" |
C++
|
The lexemes in the following table are specific to Fortran. The state stays in
LNORM.
Lexeme |
Representation |
PLUS |
"+" |
MINUS |
"-" |
STARSTAR |
"**" |
LESS |
".LT." |
LE |
".LE." |
EQ |
".EQ." |
NE |
".NE." |
GE |
".GE." |
GREATER |
".GT." |
EQ |
"==" |
NE |
"/=" |
LOGNOT |
".NOT." |
LOGAND |
".AND." |
LOGOR |
".OR." |
LOGEQV |
".EQV." |
LOGNEQV |
".NEQV." |
LOGXOR |
".XOR." |
PERCENT |
"%" |
ASSIGNOP |
"=" |
SLASHSLASH |
"//" |
OPENSLASH |
"(/" |
SLASHCLOSE |
"/)" |
LBRACKET |
"[" |
RBRACKET |
"]" |
AMPERSAND |
"&" |
SIZEOF |
sizeof |
INTEGERconstant |
".TRUE."
".FALSE."
"0"{OC}+
"0X"{HX}+
{DG}+ |
IDENTIFIER, TYPEDEFname |
{LT}({LT}|{DG})* |
FortranName |
[A-Za-z$]({LT}|{DG})*
|
FortranNamedKind |
"_"{FortranName} |
FortranNumericKind |
"_"{DG}+
|
FortranKind |
{FortranNamedKind}
{FortranNumericKind}
|
FortranCharacterNamedKind |
{FortranName}"_" |
FortranCharacterNumericKind |
{DG}+"_" |
FortranCharacterKind |
{FortranCharacterNamedKind}
{FortranCharacterNumericKind}
|
RealWithDecimal |
({DG}+"."{DG}*)
({DG}*"."{DG}+)
|
ExponentVal |
[+-]?{DG}+ |
RealEExponent |
[Ee]{ExponentVal}
|
RealDExponent |
[Dd]{ExponentVal} |
RealQExponent |
[Qq]{ExponentVal} |
RealSingleConstant |
(({DG}+{RealEExponent})
({RealWithDecimal}{RealEExponent}?)){FortranKind}?
|
RealDoubleConstant |
({DG}+|{RealWithDecimal}){RealDExponent}
|
RealQuadConstant |
({DG}+|{RealWithDecimal}){RealQExponent}
|
RealConstant |
{RealSingleConstant}
{RealDoubleConstant}
{RealQuadConstant} |
REALconstantWithKind |
RealConstant |
FortranBinaryValue |
[Bb]((['][01]+['])|(["][01]+["])) |
FortranOctalValue |
[Oo](([']{OC}+['])|(["]{OC}+["])) |
FortranHexValue |
[Zz](([']{HX}+['])|(["]{HX}+["])) |
FortranOctalValueAlternative |
(([']{OC}+['])|(["]{OC}+["]))[Oo] |
FortranHexValueAlternative |
(([']{HX}+['])|(["]{HX}+["]))[Xx] |
INTEGERconstantWithKind |
{DG}+{FortranKind}
{DG}*"#"[0-9A-Za-z]+
{FortranBinaryValue}
{FortranOctalValue}
{FortranHexValue}
{FortranOctalValueAlternative}
{FortranHexValueAlternative}
|
LOGICALconstantWithKind |
".TRUE."{FortranKind}?
".FALSE."{FortranKind}?
|
CharSingleDelim |
[^'\\\n]|('') |
CharDoubleDelim |
[^"\\\n]|("") |
FortranOctalEscape |
{OC}{1,3} |
FortranHexEscape |
[Xx]{HX}{1,2} |
FortranEscapeChar |
[\\]([AaBbFfNnRrTtVv]|{FortranOctalEscape}
|{FortranHexEscape}|0|[\\])
|
StringSingleDelim |
[']({CharSingleDelim}|[\\])*['] |
StringDoubleDelim |
["]({CharDoubleDelim}|[\\])*["] |
FortranString |
{StringSingleDelim}
{StringDoubleDelim}
|
CStringSingleDelim |
[']({CharSingleDelim}|{FortranEscapeChar})*['] |
CStringDoubleDelim |
["]({CharDoubleDelim}|{FortranEscapeChar})*["] |
FortranCString |
({CStringSingleDelim}|{CStringDoubleDelim})[Cc] |
CHARACTERconstantWithKind |
{FortranString}
{FortranCharacterKind}{FortranString}
{FortranCharacterKind}?{FortranCString}
|
The lexemes in the following table are specific to Ada. The state stays in
LNORM.
Lexeme |
Representation |
ARROW |
"->" |
INCR |
"++" |
DECR |
"--" |
LS |
"<<" |
RS |
">>" |
EQ |
"==" |
NE |
"!=" |
ANDAND |
"&&" |
OROR |
"||" |
MULTassign |
"*=" |
DIVassign |
"/=" |
MODassign |
"%=" |
PLUSassign |
"+=" |
MINUSassign |
"-=" |
LSassign |
"<<=" |
RSassign |
">>=" |
ANDassign |
"&=" |
ERassign |
"^=" |
ORassign |
"|=" |
PLUS |
"+" |
MINUS |
"-" |
MOD |
"%" |
HAT |
"^" |
AMPERSAND |
"&" |
OR |
"|" |
TWIDDLE |
"~" |
NOT |
"!" |
BRACKETS |
"[]" |
ASSIGNOP |
"=" |
LBRACKET |
"[" |
RBRACKET |
"]" |
QUESTION |
"?" |
INTEGERconstant |
"0"{OC}+
"0X"{HX}+
{DG}+ |
FLOATINGconstant |
{DG}*"."{DG}+
{DG}*"."{DG}*{Whitespace}?
{Exponent}
{DG}+{Whitespace}?{Exponent} |
IDENTIFIER, TYPEDEFname |
{LT}({LT}|{DG})* |
The lexemes in the following table are specific to COBOL. The state stays in
LNORM.
Lexeme |
Representation |
ALPHASDIGITSI18N |
[0-9A-Za-z_$\200-\377] |
ALPHASDIGITSHYPHENI18N |
[0-9\-A-Za-z_$\200-\377] |
CobolWord |
{ALPHASDIGITSI18N}|({ALPHASDIGITSI18N}{ALPHASDIGITSI18N}) |({ALPHASDIGITSI18N}+{ALPHASDIGITSHYPHENI18N}
+{ALPHASDIGITSI18N}+) |
ASSIGNOP |
"=" |
PLUS |
"+" |
MINUS |
"-" |
EXPON |
"**" |
AMPERSAND |
"&" |
AND |
AND |
MOD |
MOD |
NOT |
NOT |
OR |
OR |
REFERENCEOF |
REFERENCE([ \t]+"OF")? |
OF |
OF |
GIVING |
GIVING |
USING |
USING |
BYREFERENCE |
(BY[ \t]+)?"REFERENCE" |
BYVALUE |
(BY[ \t]+)?"VALUE" |
BYCONTENT |
(BY[ \t]+)?"CONTENT" |
BYDESCRIPTOR |
(BY[ \t]+)?"DESCRIPTOR" |
SIGNPOS |
(IS[ \t]+)?"POSITIVE"
(IS[ \t]+)?"NOT"[ \t]+"NEGATIVE" |
SIGNNEG |
(IS[ \t]+)?"NEGATIVE"
(IS[ \t]+)?"NOT"[ \t]+"POSITIVE" |
SIGNZERO |
(IS[ \t]+)?"ZERO" |
SIGNNOTZERO |
(IS[ \t]+)?"NOT"[ \t]+"ZERO" |
EQ |
(IS[ \t]+)?("="|("EQUAL"([ \t]+"TO")?)) |
LESS |
(IS[ \t]+)?("<"|("LESS"([ \t]+"THAN")?)) |
GREATER |
(IS[ \t]+)?(">"|("GREATER"([ \t]+"THAN")?)) |
NE |
(IS[ \t]+)?"NOT"[ \t]+("="|("EQUAL"([ \t]+"TO")?)) |
GE |
(IS[ \t]+)?"NOT"[ \t]+("<"|("LESS"([ \t]+"THAN")?))
(IS[ \t]+)?(">="|("GREATER"([ \t]+"THAN")?[ \t]+"OR"[ \t]+"EQUAL"([ \t]+"TO")?)) |
LE |
(IS[ \t]+)?"NOT"[ \t]+(">"|("GREATER"([ \t]+"THAN")?))
(IS[ \t]+)?("<="|("LESS" ([ \t]+"THAN")?[ \t]+"OR"[ \t]+"EQUAL"([ \t]+"TO")?)) |
DECIMALconstant |
{DecLit} |
INTEGERconstant |
{IntLit}
{CHexLit} HEXADECIMAL
{CobolHexLit} HEXADECIMAL |
FLOATINGconstant |
{FloatLit} |
IDENTIFIER |
{CobolWord} |
Most of the grammar for commands has already been given in previous sections.
This section concentrates on the grammar for expressions.
The exact syntax of expressions is specific to the current language:
expression
: expression for C
| expression for C++
| expression for Fortran
| expression for Ada
| expression for COBOL
Often you can omit an expression from a command or use a convenient default
instead, to change the meaning of a command:
expression-opt
: [ expression ]
The debugger uses the normal language lookup rules for identifiers, (obeying scopes, and so on,)
but also
extends those rules as follows:
- All global variables are visible.
- If the debugger
cannot find the identifier within the current lexical scopes, it will successively try the
lexical scopes of each of the callers.
You can subvert these rules by rescoping
the name.
NOTE: The debugger does not know where in the scope a declaration occurred, so all
lookups consider all identifiers in the scope, whether or not they occurred before the current
line.
The lexical tokens for identifiers are specific to the current language, and also to the current
lexical state.
IDENTIFIER
: identifier for LSIGNAL lexical state
| identifier for C
| identifier for C++
| identifier for Fortran
| identifier for Ada
| identifier for COBOL
TYPEDEFname
s are lexically just identifiers, but when looking them up in the
current scope, the debugger determines that they refer to types, such as
TYPEDEF
s, classes, or struct
s. This information is needed to
correctly parse C and C++ expressions.
TYPEDEFname
: IDENTIFIER
A few lexical tokens act as embedded keywords in some positions within expressions,
but the debugger generally tries to accept them as though they were normal identifiers:
identifier-or-key-word
: IDENTIFIER
| embedded-key-word
embedded-key-word
: ANY
| CHANGED
| READ
| WRITE
In other contexts, the debugger is also prepared to accept TYPEDEFname
s
(for example, int
or the name of a class
):
identifier-or-typedef-name
: identifier-or-typedef-name for C
| identifier-or-typedef-name for C++
| identifier-or-typedef-name for Fortran
| identifier-or-typedef-name for Ada
| identifier-or-typedef-name for COBOL
The lexical tokens for integer constants are specific to the current language:
integer_constant
: INTEGERconstant for C and C++
| INTEGERconstant for Fortran
| INTEGERconstant for Ada
| INTEGERconstant for COBOL
The debugger does not currently understand usages of macros, for example, uses of
C and C++ preprocessor #define
macros, and so on.
You can call any function whose
address can be taken,
provided that the parameters
can also be passed,
and the
result returned:
call-expression
: call-expression for C
| call-expression for C++
| call-expression for Fortran
| call-expression for Ada
| call-expression for COBOL
Each language may impose its own restrictions on exactly what can be passed
as a parameter.
Any expression can be passed by value, but C++ constructors and destructors
will not be invoked. Evaluating parameters can involve evaluating nested calls.
Anything whose address can be taken
can be passed by reference.
The debugger has limited understanding of array descriptors.
Comma is both the argument separator and a valid operator in C and C++. Hence,
argument lists are comma-separated
assignment-expressions
rather than
full expressions:
argument-expression-list
: assignment-expression
| assignment-expression COMMA argument-expression-list
arg-expression-list-opt
: [ argument-expression-list ]
assignment-expression
: assignment-expression for C
| assignment-expression for C++
| assignment-expression for Fortran
| assignment-expression for Ada
| assignment-expression for COBOL
Any scalar or structure type can be the return result of a called function.
Some simple array types are also supported, but the general cases are not.
The C++ constructors and destructors are not invoked, which may cause
problems.
You can take the addresses of variables and other data that are in memory,
and functions that have had code generated for them. You can also take the
address of a line of source code.
Some variables may be in registers; you cannot take their addresses.
The optimizing compilers may move variables from one memory location to another,
in which case you will obtain the address of the current memory location of
the variable.
The optimizing compilers may eliminate unused functions, as well as functions
that have had all calls inlined. Static functions in header files may result
in multiple copies of the code, and the address will be of only one of those
copies.
The optimizing compilers and linkers may skip some instructions on the way in
during a call, so a breakpoint on the first few instructions may not be hit.
When you set a breakpoint on a function, the debugger sets it deeper in the
function, at the end of the entry sequence, to try to avoid this.
The address of a line of source code is the address of the first instruction
in memory that came from this line, but this instruction may be branched around,
so it might not be executed before any other instruction
from the same line.
The debugger has extended the syntax of most languages to allow you to get the address of the first
instruction that a source line generates. If you do not specify a file via the
string
, then the current file is used. If you specify a
DOLLAR as
the line-number
, then the last line in
the file that generated any instructions is used:
line-address
: ATSIGN string COLON line-number
| ATSIGN line-number
line-number
: INTEGERconstant
| DOLLAR
Other Modified Forms of Expressions
The whatis_command
supports supersets
of the normal expression
syntax of the
language:
whatis-expressions
: whatis-expressions for C
| whatis-expressions for C++
| whatis-expressions for Fortran
| whatis-expressions for Ada
| whatis-expressions for COBOL
Some commands (notably the
examine
command
and the
cont
command) have a syntax that inhibits the use of
a full expression. In this case, a more limited form of expression is still allowed:
address-exp
: address-exp for C
| address-exp for C++
| address-exp for Fortran
| address-exp for Ada
| address-exp for COBOL
The cont
command and
the change_stack_frame_commands
have a form that specifies where to continue to, or where to cut the stack back to:
loc
: loc for C
| loc for C++
| loc for Fortran
| loc for Ada
| loc for COBOL
The target
of a
modifying_command
can only be a subset of the possible expressions,
known as a unary-expression
:
unary-expression
: unary-expression for C
| unary-expression for C++
| unary-expression for Fortran
| unary-expression for Ada
| unary-expression for COBOL
The syntax of strings is sensitive to the current lexical state and language:
string
: LNORM string
| LLINE string
| LWORD string
Most of the languages have places where they allow a series of string literals
to be equivalent to a single string formed of their concatenated characters:
string-literal-list
: string-literal-list for C
| string-literal-list for C++
| string-literal-list for Ada
Sometimes the normal language visibility rules are not sufficient for specifying the variable,
function, and so on, to which you may want to refer.
The debugger extends the language's idea of an expression with an additional possibility
called a rescoped expression.
Rescoped expressions cause the debugger to look up the
identifiers and so on in the qual-symbol-opt
,
as though it were in the source file specified by the preceding
filename-tick
or
qual-symbol-opt
:
rescoped-expression
: filename-tick qual-symbol-opt
| TICK qual-symbol-opt
rescoped-typedef
: filename-tick qual-typedef-opt
| TICK qual-typedef-opt
filename-tick
: string-tick
string-tick
: string TICK
qual-symbol-opt
: expression /* Base (global) name */
| qual-symbol-opt TICK expression /* Qualified name */
qual-typedef-opt
: qual-typedef-opt for C
| qual-typedef-opt for C++
| qual-typedef-opt for Fortran
| qual-typedef-opt for Ada
| qual-typedef-opt for COBOL
In the following example, rescoped expressions are used to distinguish which
x
the user is querying, because there are two variables named
x
(one local to main
and one global):
(ladebug) list $curline - 10: 20
1 long x = 5; // global x
2
3 int main()
4 {
5 int x = 7; // local x
6 int y = x - ::x;
> 7 return (y);
8 }
By default, a local variable is found before a global one,
so that the plain x
refers to the local variable:
(ladebug) whatis x
int x
(ladebug) which x
"x_rescoped.cxx"`main`x
(ladebug) whatis "x_rescoped.cxx"`main`x
int x
You can use the C++ ::
operator to specify the global
x
in C++ code or rescoped expressions in any language:
,
(ladebug) whatis ::x
long x
(ladebug) whatis "x_rescoped.cxx"`x
long x
(ladebug) print "x_rescoped.cxx"`x
5
In the following example,
the x
variable is used in the following places to
demonstrate how rescoping expressions can find the correct variable:
- As a variable local to
main
- As a member variable of the class
Foo
- As a global
variable
- As a local variable to
Foo
's member function SetandGet
- As a local variable to the
CastAndAdd
function, but
visible as a parameter
(ladebug) list $curline - 10: 20
10 double x = 3.1415;
11
12 int CastAndAdd(char x) {
13 int result = ((int)::x) + x;
14 return result;
15 }
16
17 float Foo::SetandGet() { // multiple scopes!
18 long x = (long)::x; // local x = global x
19 Foo::x = (float)x; // member x = local x
> 20 return Foo::x; // return member x
21 }
22
23 int main () {
24 int x = 7;
25 x -= CastAndAdd((char)1);
26
27 Foo thefoo;
28 x -= (int)thefoo.SetandGet();
29 return x;
(ladebug) whatis x
long x
(ladebug) which x
"x_rescoped2.cxx"`Foo::SetandGet`x
(ladebug) whatis ::x
double x
(ladebug) whatis Foo::x
float Foo::x
(ladebug) whatis main`x
int x
(ladebug) whatis CastAndAdd`x
char x
When debugging a Fortran 90 program, you can refer to the components of a
module using rescoped expressions. For example, when SERIES
is a
module in the file fft.f90
, then "fft.f90"`SERIES`ORDER
refers to the component
ORDER
in the module SERIES
.
The lexical tokens for printable types are specific to the current language:
printable-type
: printable-type for C
| printable-type for C++
| printable-type for Fortran
| printable-type for Ada
| printable-type for COBOL
The debugger has an almost complete understanding of C expressions, given the
general restrictions:
expression
: assignment-expression
constant-expression
: conditional-expression
C Identifiers
The lookup rules are almost always correct for C:
identifier-or-typedef-name
: identifier-or-key-word
| TYPEDEFname
C Constants
The numeric constants are treated exactly the same as in C. The enumeration
constant identifiers go through the same grammar paths as variable identifiers,
which produces basically the same effect as the C semantics:
primary-expression
: identifier-or-key-word
| constant
| string-literal-list
| LPAREN expression RPAREN
| process_set
| LPAREN process_range RPAREN
string-literal-list
: string
| string-literal-list string
constant
: FLOATINGconstant
| INTEGERconstant
| CHARACTERconstant
| WIDECHARACTERconstant
| WIDESTRINGliteral
C Rescoped Expressions
The C implementation of
rescoped expressions is the following:
qual-typedef-opt
: TYPEDEFname
| qual-typedef-opt TICK TYPEDEFname
whatis-expressions
: expression
| rescoped-expression
| printable-type
C Calls
Following is the C implementation of calls:
call-expression
: expression
function-call
: postfix-expression LPAREN [arg-expression-list] RPAREN
Restrictions and limits are documented here.
C Addresses
Following is the C implementation of addresses:
address
: AMPERSAND postfix-expression
| line-address
| postfix-expression
address-exp
: address
| address-exp PLUS address
| address-exp MINUS address
| address-exp STAR address
Restrictions and limits are documented here.
C Loc Specifications
The C implementation of
loc
is the following:
loc
: expression
| rescoped-expression
C Types
The debugger understands the full C type specification grammar:
type-specifier
: basic-type-specifier
| struct-union-enum-type-specifier
| typedef-type-specifier
basic-type-specifier
: basic-type-name
| type-qualifier-list basic-type-name
| basic-type-specifier type-qualifier
| basic-type-specifier basic-type-name
type-qualifier-list
: type-qualifier
| type-qualifier-list type-qualifier
type-qualifier
: CONST
| VOLATILE
basic-type-name
: VOID
| CHAR
| SHORT
| INT
| LONG
| FLOAT
| DOUBLE
| SIGNED
| UNSIGNED
printable-type
: rescoped_typedef
| type_name
struct-union-enum-type-specifier
: elaborated-type-name
| type-qualifier-list elaborated-type-name
| struct-union-enum-type-specifier type-qualifier
typedef-type-specifier
: TYPEDEFname
| type-qualifier-list TYPEDEFname
| typedef-type-specifier type-qualifier
elaborated-type-name
: struct-or-union-specifier
| enum-specifier
struct-or-union-specifier
: struct-or-union opt-parenthesized-identifier-or-typedef-name
opt-parenthesized-identifier-or-typedef-name
: identifier-or-typedef-name
| LPAREN opt-parenthesized-identifier-or-typedef-name RPAREN
struct-or-union
: STRUCT
| UNION
enum-specifier
: ENUM identifier-or-typedef-name
type-name
: type-specifier
| type-specifier abstract-declarator
| type-qualifier-list // Implicit "int"
| type-qualifier-list abstract-declarator // Implicit "int"
type-name-list
: type-name
| type-name COMMA type-name-list
abstract-declarator
: unary-abstract-declarator
| postfix-abstract-declarator
| postfixing-abstract-declarator
postfixing-abstract-declarator
: array-abstract-declarator
| LPAREN RPAREN
array-abstract-declarator
: BRACKETS
| LBRACKET constant-expression RBRACKET
| array-abstract-declarator LBRACKET constant-expression RBRACKET
unary-abstract-declarator
: STAR
| STAR type-qualifier-list
| STAR abstract-declarator
| STAR type-qualifier-list abstract-declarator
postfix-abstract-declarator
: LPAREN unary-abstract-declarator RPAREN
| LPAREN postfix-abstract-declarator RPAREN
| LPAREN postfixing-abstract-declarator RPAREN
| LPAREN unary-abstract-declarator RPAREN postfixing-abstract-declarator
C Other Forms of Expressions
The following expressions all have their usual C semantics:
assignment-expression
: conditional-expression
| unary-expression ASSIGNOP assignment-expression
| unary-expression MULTassign assignment-expression
| unary-expression DIVassign assignment-expression
| unary-expression MODassign assignment-expression
| unary-expression PLUSassign assignment-expression
| unary-expression MINUSassign assignment-expression
| unary-expression LSassign assignment-expression
| unary-expression RSassign assignment-expression
| unary-expression ANDassign assignment-expression
| unary-expression ERassign assignment-expression
| unary-expression ORassign assignment-expression
conditional-expression
: logical-OR-expression
| logical-OR-expression QUESTION expression COLON conditional-expression
logical-OR-expression
: logical-AND-expression
| logical-OR-expression OROR logical-AND-expression
logical-AND-expression
: inclusive-OR-expression
| logical-AND-expression ANDAND inclusive-OR-expression
inclusive-OR-expression
: exclusive-OR-expression
| inclusive-OR-expression OR exclusive-OR-expression
exclusive-OR-expression
: AND-expression
| exclusive-OR-expression HAT AND-expression
AND-expression
: equality-expression
| AND-expression AMPERSAND equality-expression
equality-expression
: relational-expression
| equality-expression EQ relational-expression
| equality-expression NE relational-expression
relational-expression
: shift-expression
| relational-expression LESS shift-expression
| relational-expression GREATER shift-expression
| relational-expression LE shift-expression
| relational-expression GE shift-expression
shift-expression
: additive-expression
| shift-expression LS additive-expression
| shift-expression RS additive-expression
additive-expression
: multiplicative-expression
| additive-expression PLUS multiplicative-expression
| additive-expression MINUS multiplicative-expression
multiplicative-expression
: cast-expression
| multiplicative-expression STAR cast-expression
| multiplicative-expression SLASH cast-expression
| multiplicative-expression MOD cast-expression
cast-expression
: unary-expression
| LPAREN type-name RPAREN cast-expression
unary-expression
: postfix-expression
| INCR unary-expression
| DECR unary-expression
| AMPERSAND cast-expression
| STAR cast-expression
| PLUS cast-expression
| MINUS cast-expression
| TWIDDLE cast-expression
| NOT cast-expression
| SIZEOF unary-expression
| SIZEOF LPAREN type-name RPAREN
| line-address
postfix-expression
: primary-expression
| postfix-expression LBRACKET expression RBRACKET
| function-call
| postfix-expression LPAREN type-name-list RPAREN
| postfix-expression DOT identifier-or-typedef-name
| postfix-expression ARROW identifier-or-typedef-name
| postfix-expression INCR
| postfix-expression DECR
C++ is a complex language, with a rich expression system. The debugger
understands much of the system, but it does not understand how to evaluate
some complex aspects of a C++ expression. It can correctly debug these when
they occur in the source code.
The aspects of the expression system not processed properly during debugger expression evaluation
include the following:
- Many of the implicit conversions
- Program-defined operators
- Calling constructors and destructors during the debugger's own evaluation of
expressions
There are also some minor restrictions in the following grammar, compared with
the full C++ expression grammar, to make it unambiguous:
expression
: assignment-expression
constant-expression
: conditional-expression
C++ Identifiers
The debugger correctly augments the general
lookup rules when inside
class member functions, to look up the members correctly.
The debugger has only a limited understanding of namespaces. It correctly processes
names such as UserNameSpace::NestedNamespace::userIdentifier
, as
well as C++ use-declarations, which introduce a new
identifier into a scope.
The debugger does not currently understand C++ using-directives.
The debugger understands the relationship between struct
and class identifiers and
typedef
identifiers:
id-or-keyword-or-typedef-name
: identifier-or-key-word
| TYPEDEFname
C++ Constants
The debugger treats numeric constants the same as C++ does. The enumeration
constant identifiers go though the same grammar paths as variable identifiers,
which produces basically the same effect as the C++ semantics:
constant
: FLOATINGconstant
| INTEGERconstant
| CHARACTERconstant
| WIDECHARACTERconstant
| WIDESTRINGliteral
C++ Calls
The debugger does not understand the following aspects of C++ calls:
- Invoking C++ constructors and destructors to create and destroy temporaries
containing the value of parameters and results.
- Default parameters.
- Many of the implicit conversions that may be needed for the parameters.
- Overloading resolution. Instead, the debugger queries the user.
call-expression
: expression
Restrictions and limits are documented here.
C++ Addresses
Following is the C++ implementation of addresses:
address
: AMPERSAND postfix-expression /* Address of */
| line-address
| postfix-expression
address-exp
: address
| address-exp PLUS address
| address-exp MINUS address
| address-exp STAR address
Restrictions and limits are documented here.
C++ Loc
Following is the C++ implementation of
loc
:
loc
: expression
| rescoped-expression
C++ Other Modified Forms of Expressions
whatis-expressions
: expression
| printable-type
C++ Rescoped Expressions
The C++ implementation of
rescoped expressions is as follows:
qual-typedef-opt
: type-name
| qual-typedef-opt TICK type-name
C++ Strings
The C++ implementation of strings is as follows:
string-literal-list
: string
| string-literal-list string
C++ Identifier Expressions
The debugger understands nested names. Namespaces go through the same paths
as classes, hence the unusual use of TYPEDEFname:
id-expression
: id-expression-internals
id-expression-internals
: qualified-id
| id-or-keyword-or-typedef-name
| operator-function-name
| TWIDDLE id-or-keyword-or-typedef-name
qualified-id
: nested-name-specifier qualified-id-follower
qualified-type
: nested-name-specifier TYPEDEFname
nested-name-specifier
: CLCL
| TYPEDEFname CLCL
| nested-name-specifier TYPEDEFname CLCL
qualified-id-follower
: identifier-or-key-word
| operator-function-name
| TWIDDLE id-or-keyword-or-typedef-name
C++ Types
The debugger understands the full C++ type specification grammar:
type-specifier
: basic-type-specifier
| struct-union-enum-type-specifier
| typedef-type-specifier
type-qualifier-list
: type-qualifier
| type-qualifier-list type-qualifier
type-qualifier
: CONST
| VOLATILE
basic-type-specifier
: basic-type-name basic-type-name
| basic-type-name type-qualifier
| type-qualifier-list basic-type-name
| basic-type-specifier type-qualifier
| basic-type-specifier basic-type-name
struct-union-enum-type-specifier
: elaborated-type-name
| type-qualifier-list elaborated-type-name
| struct-union-enum-type-specifier type-qualifier
typedef-type-specifier
: TYPEDEFname type-qualifier
| type-qualifier-list TYPEDEFname
| typedef-type-specifier type-qualifier
basic-type-name
: VOID
| CHAR
| SHORT
| INT
| LONG
| FLOAT
| DOUBLE
| SIGNED
| UNSIGNED
elaborated-type-name
: aggregate-name
| enum-name
printable-type
: rescoped-typedef
| type-name
aggregate-name
: aggregate-key opt-parenthesized-identifier-or-typedef-name
| aggregate-key qualified-type
opt-parenthesized-identifier-or-typedef-name
: id-or-keyword-or-typedef-name
| LPAREN opt-parenthesized-identifier-or-typedef-name RPAREN
aggregate-key
: STRUCT
| UNION
| CLASS
enum-name
: ENUM id-or-keyword-or-typedef-name
parameter-type-list
: PARENS type-qualifier-list-opt
type-name
: type-specifier
| qualified-type
| basic-type-name
| TYPEDEFname
| type-qualifier-list
| type-specifier abstract-declarator
| basic-type-name abstract-declarator
| qualified-type abstract-declarator
| TYPEDEFname abstract-declarator
| type-qualifier-list abstract-declarator
abstract-declarator
: unary-abstract-declarator
| postfix-abstract-declarator
| postfixing-abstract-declarator
postfixing-abstract-declarator
: array-abstract-declarator
| parameter-type-list
array-abstract-declarator
: BRACKETS
| LBRACKET constant-expression RBRACKET
| array-abstract-declarator LBRACKET constant-expression RBRACKET
unary-abstract-declarator
: STAR
| AMPERSAND
| pointer-operator-type
| STAR abstract-declarator
| AMPERSAND abstract-declarator
| pointer-operator-type abstract-declarator
postfix-abstract-declarator
: LPAREN unary-abstract-declarator RPAREN
| LPAREN postfix-abstract-declarator RPAREN
| LPAREN postfixing-abstract-declarator RPAREN
| LPAREN unary-abstract-declarator RPAREN postfixing-abstract-declarator
pointer-operator-type
: TYPEDEFname CLCL STAR type-qualifier-list-opt
| STAR type-qualifier-list
| AMPERSAND type-qualifier-list
C++ Other Forms of Expressions
The following expressions all have the usual C++ semantics:
primary-expression
: constant
| string-literal-list
| THIS
| LPAREN expression RPAREN
| operator-function-name
| identifier-or-key-word
| qualified-id
| process_set
| LPAREN process_range RPAREN
operator-function-name
: OPERATOR operator-predefined
| OPERATOR basic-type-name
| OPERATOR TYPEDEFname
| OPERATOR LPAREN type-name RPAREN
| OPERATOR type-qualifier
| OPERATOR qualified-type
operator-predefined
: PLUS
| MINUS
| STAR
| ...
| DELETE
| COMMA
type-qualifier-list-opt
: [ type-qualifier-list ]
postfix-expression
: primary-expression
| postfix-expression LBRACKET expression RBRACKET
| postfix-expression PARENS
| postfix-expression LPAREN argument-expression-list RPAREN
| postfix-expression LPAREN type-name-list RPAREN
| postfix-expression DOT id-expression
| postfix-expression ARROW id-expression
| postfix-expression INCR
| postfix-expression DECR
| TYPEDEFname LPAREN argument-expression-list RPAREN
| TYPEDEFname LPAREN type-name-list RPAREN
| basic-type-name LPAREN assignment-expression RPAREN
type-name-list
: type-name
| type-name COMMA type-name-list
| type-name comma-opt-ellipsis
| ELLIPSIS
comma-opt-ellipsis
: ELLIPSIS
| COMMA ELLIPSIS
unary-expression
: postfix-expression
| INCR unary-expression
| DECR unary-expression
| line-address
| AMPERSAND cast-expression
| STAR cast-expression
| MINUS cast-expression
| PLUS cast-expression
| TWIDDLE LPAREN cast-expression RPAREN
| NOT cast-expression
| SIZEOF unary-expression
| SIZEOF LPAREN type-name RPAREN
| allocation-expression
allocation-expression
: operator-new LPAREN type-name RPAREN operator-new-initializer
| operator-new LPAREN argument-expression-list RPAREN LPAREN type-name RPAREN operator-new-initializer
operator-new
: NEW
| CLCL NEW
operator-new-initializer
: [ PARENS ]
| [ LPAREN argument-expression-list RPAREN ]
cast-expression
: unary-expression
| LPAREN type-name RPAREN cast-expression
deallocation-expression
: cast-expression
| DELETE deallocation-expression
| CLCL DELETE deallocation-expression
| DELETE BRACKETS deallocation-expression
| CLCL DELETE BRACKETS deallocation-expression
point-member-expression
: deallocation-expression
| point-member-expression DOTstar deallocation-expression
| point-member-expression ARROWstar deallocation-expression
multiplicative-expression
: point-member-expression
| multiplicative-expression STAR point-member-expression
| multiplicative-expression SLASH point-member-expression
| multiplicative-expression MOD point-member-expression
additive-expression
: multiplicative-expression
| additive-expression PLUS multiplicative-expression
| additive-expression MINUS multiplicative-expression
shift-expression
: additive-expression
| shift-expression LS additive-expression
| shift-expression RS additive-expression
relational-expression
: shift-expression
| relational-expression LESS shift-expression
| relational-expression GREATER shift-expression
| relational-expression LE shift-expression
| relational-expression GE shift-expression
equality-expression
: relational-expression
| equality-expression EQ relational-expression
| equality-expression NE relational-expression
AND-expression
: equality-expression
| AND-expression AMPERSAND equality-expression
exclusive-OR-expression
: AND-expression
| exclusive-OR-expression HAT AND-expression
inclusive-OR-expression
: exclusive-OR-expression
| inclusive-OR-expression OR exclusive-OR-expression
logical-AND-expression
: inclusive-OR-expression
| logical-AND-expression ANDAND inclusive-OR-expression
logical-OR-expression
: logical-AND-expression
| logical-OR-expression OROR logical-AND-expression
conditional-expression
: logical-OR-expression
| logical-OR-expression QUESTION expression COLON conditional-expression
assignment-expression
: conditional-expression
| unary-expression ASSIGNOP assignment-expression
| unary-expression MULTassign assignment-expression
| unary-expression DIVassign assignment-expression
| unary-expression MODassign assignment-expression
| unary-expression PLUSassign assignment-expression
| unary-expression MINUSassign assignment-expression
| unary-expression LSassign assignment-expression
| unary-expression RSassign assignment-expression
| unary-expression ANDassign assignment-expression
| unary-expression ERassign assignment-expression
| unary-expression ORassign assignment-expression
This section contains expressions specific to Fortran.
Fortran Identifiers
The Fortran implementation of identifiers is as follows:
identifier-or-typedef-name
: identifier-or-key-word
| TYPEDEFname
| PROCEDUREname
Fortran Constants
The Fortran implementation of constants is as follows:
real-or-imag-part
: real_constant
| PLUS real_constant
| MINUS real_constant
| integer_constant
| PLUS integer_constant
| MINUS integer_constant
constant
: real_constant
| integer_constant
| complex-constant
| character_constant
| LOGICALconstantWithKind
character_constant
: CHARACTERconstantWithKind
| string
complex-constant
: LPAREN real-or-imag-part COMMA real-or-imag-part RPAREN
Fortran Rescoped Expressions
The Fortran implementation of rescoped expressions is
as follows:
qual-typedef-opt
: TYPEDEFname /* Base (global) name */
| qual-typedef-opt TICK TYPEDEFname /* Qualified name */
whatis-expressions
: expression
| rescoped-expression
| printable_type
Fortran Calls
The Fortran implementation of calls is as follows:
call-expression
: call-stmt
call-stmt
: named-subroutine
| named-subroutine LPAREN RPAREN
| named-subroutine LPAREN actual-arg-spec-list RPAREN
Fortran Addresses
The Fortran implementation of addresses is as follows:
address
: line-address
| primary
address-exp
: address
| address-exp PLUS address
| address-exp MINUS address
| address-exp STAR address
Restrictions and limits are documented here.
Fortran Loc
The Fortran implementation of loc
is as follows:
loc
: expression
| rescoped-expression
Fortran Types
The Fortran implementation of types is as follows:
type-name
: TYPEDEFname
printable-type
: rescoped-typedef
| type-name
Other Forms of Fortran Expressions
Following are other forms of Fortran expressions:
expression
: expr
| named-procedure
assignment-expression
: expr
constant-expression
: constant
unary-expression
: variable
expr
: level-5-expr
| expr defined-binary-op level-5-expr
level-5-expr
: equiv-operand
| level-5-expr LOGEQV equiv-operand
| level-5-expr LOGNEQV equiv-operand
| level-5-expr LOGXOR equiv-operand
equiv-operand
: or-operand
| equiv-operand LOGOR or-operand
or-operand
: and-operand
| or-operand LOGAND and-operand
and-operand
: level-4-expr
| LOGNOT and-operand
level-4-expr
: level-3-expr
| level-3-expr LESS level-3-expr
| level-3-expr GREATER level-3-expr
| level-3-expr LE level-3-expr
| level-3-expr GE level-3-expr
| level-3-expr EQ level-3-expr
| level-3-expr NE level-3-expr
level-3-expr
: level-2-expr
| level-3-expr SLASHSLASH level-2-expr
level-2-expr
: add-operand
| level-2-expr PLUS add-operand
| level-2-expr MINUS add-operand
add-operand
: add-operand-f90
| add-operand-dec
| unary-expr-dec
add-operand-f90
: mult-operand-f90
| add-operand-f90 STAR mult-operand-f90
| add-operand-f90 SLASH mult-operand-f90
mult-operand-f90
: level-1-expr
| level-1-expr STARSTAR mult-operand-f90
add-operand-dec
: mult-operand-dec
| add-operand-f90 STAR mult-operand-dec
| add-operand-f90 SLASH mult-operand-dec
| add-operand-f90 STAR unary-expr-dec
| add-operand-f90 SLASH unary-expr-dec
mult-operand-dec
: level-1-expr STARSTAR mult-operand-dec
| level-1-expr STARSTAR unary-expr-dec
unary-expr-dec
: PLUS add-operand
| MINUS add-operand
level-1-expr
: primary
| defined-unary-op primary
defined-unary-op
: DOT_LETTERS_DOT
primary
: constant
| variable
| function-reference
| LPAREN expr RPAREN
| AMPERSAND variable
| process_set
| LPAREN process_range RPAREN
defined-binary-op
: DOT_LETTERS_DOT
int-expr
: expr
scalar-int-expr
: int-expr
variable
: named-variable
| subobject
named-variable
: variable-name
subobject
: array-elt-or-sect
| structure-component
| known-substring
known-substring
: disabled-array-elt-or-sect LPAREN substring-range RPAREN
| hf-array-abomination
substring-range
: scalar-int-expr COLON scalar-int-expr
| scalar-int-expr COLON
| COLON scalar-int-expr
| COLON
hf-array-abomination
: named-variable
LPAREN section-subscript-list RPAREN
LPAREN section-subscript RPAREN
| structure PERCENT any-identifier
LPAREN section-subscript-list RPAREN
LPAREN section-subscript RPAREN
| structure DOT any-identifier
LPAREN section-subscript-list RPAREN
LPAREN section-subscript RPAREN
disabled-array-elt-or-sect
: DISABLER array-elt-or-sect
array-elt-or-sect
: named-variable LPAREN section-subscript-list RPAREN
| structure PERCENT any-identifier LPAREN section-subscript-list RPAREN
| structure DOT any-identifier LPAREN section-subscript-list RPAREN
section-subscript-list
: section-subscript
| section-subscript COMMA section-subscript-list
subscript
: scalar-int-expr
section-subscript
: subscript
| subscript-triplet
subscript-triplet
: subscript COLON subscript COLON stride
| subscript COLON COLON stride
| COLON subscript COLON stride
| COLON COLON stride
| subscript COLON subscript
| subscript COLON
| COLON subscript
| COLON
stride
: scalar-int-expr
structure-component
: structure PERCENT any-identifier
| structure DOT any-identifier
structure
: named-variable
| structure-component
| array-elt-or-sect
function-reference
: SIZEOF LPAREN expr RPAREN
| named-function LPAREN RPAREN
| named-function LPAREN actual-arg-spec-list RPAREN
named-procedure
: PROCEDUREname
named-function
: PROCEDUREname
named-subroutine
: PROCEDUREname
actual-arg-spec-list
: actual-arg-spec
| actual-arg-spec COMMA actual-arg-spec-list
actual-arg-spec
: actual-arg
actual-arg
: expr
any-identifier
: variable-name
| PROCEDUREname
variable-name
: identifier-or-key-word
PROCEDUREname
: IDENTIFIER
This section contains expressions specific to Ada.
Ada Constants
The Ada implementation of constants is as follows.
NOTE: ENUMERATIONconstant is not included in this section because
it is like a variable with a type of "enumeration constant."
constant
: FLOATINGconstant
| INTEGERconstant
| CHARACTERconstant
Ada Rescoped Expressions
The Ada implementation of rescoped expressions is as follows:
qual-typedef-opt
: TYPEDEFname
| qual-symbol-opt TICK TYPEDEFname
whatis-expressions
: expression
| rescoped-expression
| printable_type
Ada Calls
The Ada implementation of calls is as follows:
call-expression
: expression
Ada Addresses
The Ada implementation of addresses is as follows:
address
: line-address
| AMPERSAND postfix_expression
| LPAREN postfix_expression RPAREN
address-exp
: address
| address-exp PLUS address
| address-exp MINUS address
| address-exp STAR address
Ada Loc Specification
The Ada implementation of loc
is as follows:
loc
: expression
| rescoped-expression
Ada Types
The Ada implementation of types is as follows:
type-specifier
: typedef-type-specifier
typedef-type-specifier
: TYPEDEFname
identifier-or-typedef-name
: identifier-or-key-word
| TYPEDEFname
type-name
: type-specifier
| type-specifier abstract-declarator
printable-type
: rescoped-typedef
| type-name
Other Forms of Ada Expressions
Following are other forms of Ada expressions:
primary-expression
: identifier-or-key-word
| constant
| string-literal-list
| LPAREN expression RPAREN
| process_set
| LPAREN process_range RPAREN
postfix-expression
: primary-expression
| postfix-expression LBRACKET expression RBRACKET
| postfix-expression LPAREN arg-expression-list-opt RPAREN
| postfix-expression DOT identifier-or-typedef-name
| postfix-expression ARROW identifier-or-typedef-name
| postfix-expression INCR
| postfix-expression DECR
string-literal-list
: string
| string-literal-list string
unary-expression
: postfix-expression
| INCR unary-expression
| DECR unary-expression
| AMPERSAND cast-expression
| line-address
| STAR cast-expression
| PLUS cast-expression
| MINUS cast-expression
| TWIDDLE cast-expression
| NOT cast-expression
cast-expression
: unary-expression
| LPAREN type-name RPAREN cast-expression
multiplicative-expression
: cast-expression
| multiplicative-expression STAR cast-expression
| multiplicative-expression SLASH cast-expression
| multiplicative-expression MOD cast-expression
additive-expression
: multiplicative-expression
| additive-expression PLUS multiplicative-expression
| additive-expression MINUS multiplicative-expression
shift-expression
: additive-expression
| shift-expression LS additive-expression
| shift-expression RS additive-expression
relational-expression
: shift-expression
| relational-expression LESS shift-expression
| relational-expression GREATER shift-expression
| relational-expression LE shift-expression
| relational-expression GE shift-expression
equality-expression
: relational-expression
| equality-expression EQ relational-expression
| equality-expression NE relational-expression
AND-expression
: equality-expression
| AND-expression AMPERSAND equality-expression
exclusive-OR-expression
: AND-expression
| exclusive-OR-expression HAT AND-expression
inclusive-OR-expression
: exclusive-OR-expression
| inclusive-OR-expression OR exclusive-OR-expression
logical-AND-expression
: inclusive-OR-expression
| logical-AND-expression ANDAND inclusive-OR-expression
logical-OR-expression
: logical-AND-expression
| logical-OR-expression OROR logical-AND-expression
conditional-expression
: logical-OR-expression
| logical-OR-expression QUESTION expression COLON conditional-expression
assignment-expression
: conditional-expression
| unary-expression ASSIGNOP assignment-expression
| unary-expression MULTassign assignment-expression
| unary-expression DIVassign assignment-expression
| unary-expression MODassign assignment-expression
| unary-expression PLUSassign assignment-expression
| unary-expression MINUSassign assignment-expression
| unary-expression LSassign assignment-expression
| unary-expression RSassign assignment-expression
| unary-expression ANDassign assignment-expression
| unary-expression ERassign assignment-expression
| unary-expression ORassign assignment-expression
expression
: assignment-expression
constant-expression
: conditional-expression
abstract-declarator
: unary-abstract-declarator
| postfix-abstract-declarator
| postfixing-abstract-declarator
postfixing-abstract-declarator
: array-abstract-declarator
| LPAREN RPAREN
array-abstract-declarator
: BRACKETS
| LBRACKET constant-expression RBRACKET
| array-abstract-declarator LBRACKET constant-expression RBRACKET
unary-abstract-declarator
: STAR
| STAR abstract-declarator
postfix-abstract-declarator
: LPAREN unary-abstract-declarator RPAREN
| LPAREN postfix-abstract-declarator RPAREN
| LPAREN postfixing-abstract-declarator RPAREN
| LPAREN unary-abstract-declarator RPAREN postfixing-abstract-declarator
This section contains expressions specific to COBOL.
COBOL Constants
The COBOL implementation of constants is as follows:
constant
: FLOATINGconstant
| INTEGERconstant
| DECIMALconstant
| CHARACTERconstant
constant-expression
: cobol-expression
COBOL Rescoped Expressions
The COBOL implementation of rescoped expressions is as follows:
qual-typedef-opt
: TYPEDEFname
| qual-typedef-opt TICK TYPEDEFname
COBOL Calls
The COBOL implementation of calls is as follows:
call-expression
: identifier-or-key-word
| identifier-or-key-word USING cobol-expression-list
COBOL Addresses
The COBOL implementation of addresses is as follows:
address
: INTEGERconstant
| line-address
| address-language
| LPAREN cobol-expression RPAREN
address-exp
: address
| address-exp PLUS address
| address-exp MINUS address
| address-exp STAR address
address-language
: AMPERSAND cobol-identifier
| REFERENCEOF cobol-identifier
COBOL Loc
The COBOL implementation of loc is as follows:
loc
: cobol-identifier
| rescoped-expression
COBOL Types
The COBOL implementation of types is as follows:
printable-type
: rescoped-typedef
Other Forms of COBOL Expressions
Following are other forms of COBOL expressions:
assignment-expression
: expression
cobol-expression
: cobol-identifier
| constant
| string
| cobol-expression PLUS cobol-expression
| cobol-expression MINUS cobol-expression
| cobol-expression STAR cobol-expression
| cobol-expression SLASH cobol-expression
| cobol-expression EXPON cobol-expression
| MINUS cobol-expression
| PLUS cobol-expression
| LPAREN cobol-expression RPAREN
| process_set
| LPAREN process_range RPAREN
cobol-expression-list
: cobol-expression
| cobol-expression COMMA cobol-expression-list
cobol-identifier
: qualification
| subscript
| refmod
condition-expression
: combined-condition
| negated-simple-condition
combined-condition
: negated-simple-condition AND negated-simple-condition
| negated-simple-condition OR negated-simple-condition
| LPAREN combined-condition RPAREN
negated-simple-condition
: simple-condition
| NOT simple-condition
| LPAREN NOT simple-condition RPAREN
simple-condition
: cobol-expression EQ cobol-expression
| cobol-expression ASSIGNOP cobol-expression
| cobol-expression NE cobol-expression
| cobol-expression LESS cobol-expression
| cobol-expression GREATER cobol-expression
| cobol-expression LE cobol-expression
| cobol-expression GE cobol-expression
| cobol-expression SIGNPOS
| cobol-expression SIGNNEG
| cobol-expression SIGNZERO
| cobol-expression SIGNNOTZERO
| LPAREN simple-condition RPAREN
expression
: constant-expression
| condition-expression
| address-language
identifier-or-typedef-name
: identifier-or-key-word
lvalue-expression
: cobol-identifier
qualification
: identifier-or-key-word OF qualification
| identifier-or-key-word
refmod
: qualification LPAREN cobol-expression COLON RPAREN
| qualification LPAREN cobol-expression COLON cobol-expression RPAREN
| subscript LPAREN cobol-expression COLON RPAREN
| subscript LPAREN cobol-expression COLON cobol-expression RPAREN
subscript
: qualification LPAREN cobol-expression-list RPAREN
unary-expression
: lvalue-expression
whatis-expressions
: expression
| rescoped-expression
| rescoped-typedef
When the operating system encounters an unrecoverable error while running a
process, for example a segmentation violation (SEGV),
the system creates a file named core
and places it in the current directory.
The core file is not an executable file; it is a snaphot of the state of
your process at the time the error occurred. It allows you to analyze the
process at the point it crashed.
This chapter discusses the following topics:
It also contains a core file debugging example
and a quick reference for transporting
a core file.
You can use the debugger to examine the process information in a core file. Use
the following Ladebug command syntax to invoke the debugger on a core file:
% ladebug executable_file core_file
or
(ladebug) load executable_file core_file
The executable file is that which was being executed at the time the core file
was generated.
When debugging a core file, you can use the debugger to obtain a stack trace and the
values of some variables, just as you would for a stopped process.
The stack trace lists the functions in your program that were active when the
dump occurred. By examining the values of a few variables along with
the stack trace, you may be able to pinpoint the process state and the cause
of the core dump. Core files cannot be executed; therefore the rerun
,
step
, cont
, and other commands will not work until you create
a process using the run
command.
In addition, if the program is multithreaded, you can examine the thread
information with the
show thread
and
thread
commands. You can
examine the stack trace for a particular thread or for all threads with the
where thread
command.
The following example uses a null pointer reference in the factorial
function.
This reference causes the process to abort and dump the core when it is
executed. The dump
command prints the value of the x
variable
as a null, and the print *x
command reveals that you cannot
dereference a null pointer.
% cat testProgram.c
#include <stdio.h>
int factorial(int i)
main() {
int i,f;
for (i=1 ; i<3 ; i++) {
f = factorial(i);
printf("%d! = %d\en",i,f);
}
}
int factorial(int i)
int i;
{
int *x;
x = 0;
printf("%d",*x);
if (i<=1)
return (1);
else
return (i * factorial(i-1) );
}
% cc -o testProgram -g testProgram.c
% testProgram
Memory fault - core dumped.
% ladebug testProgram core
Welcome to the Ladebug Debugger Version n
------------------
object file name: testProgram
core file name: core
Reading symbolic information ...done
Core file produced from executable testProgram
Thread terminated at PC 0x120000dc4 by signal SEGV
(ladebug) where
>0 0x120000dc4 in factorial(i=1) testProgram.c:13
#1 0x120000d44 in main() testProgram.c:4
(ladebug) dump
>0 0x120000dc4 in factorial(i=1) testProgram.c:13
printf("%d",*x);
(ladebug) print *x
Cannot dereference 0x0
Error: no value for *x
(ladebug)
Transporting core files is usually necessary to debug a core file on a system
other than that which produced it. It is sometimes possible to debug a
core file on a system other than that which produced it if the current system
is sufficiently similar to the original system, but it will not work correctly
in general.
The following procedure (see also quick reference)
shows how to transport the core files, including the Compaq POSIX Threads Library.
In this example, a.out
is the name of the executable and
core
is the name of the core file.
You need to collect a variety of files from the original system. These
include the executable, the core file, shared libraries used by the
executable, and /usr/shlib/libpthreaddebug.so
if the POSIX Threads
Library is involved.
Do the following steps (1 through 4) on the original system:
- Determine the shared objects in use:
% ladebug a.out core
(ladebug) listobj
(ladebug) quit
- Cut, paste, and edit the result into a list of file names. Most will probably
begin with
/usr/shlib/
.
- If
/usr/shlib/libpthread.so
is one of the files, add
/usr/shlib/libpthreaddebug.so
to the list.
(If you have a privately delivered libpthread.so
, there
should be a privately delivered corresponding libpthreaddebug.so
; use
the privately delivered one.)
- Package the
a.out
, core
and shared objects,
for example, into a
tar
file. Be sure to use the tar h
option to force tar
to
follow symbolic links as if they were normal files or directories.
% tar cfvh mybug.tar
Then do the following steps (5 through 14) on the current system:
On the current system, the executable and core file are generally put in the
current working directory, the shared objects are put in an application
subdirectory, and libpthreaddebug.so
is put in a debugger
subdirectory.
- Create a directory for debugging the transported core files:
% mkdir mybug
- Move to that directory:
% cd mybug
- Get the package:
% mv <wherever>/mybug.tar .
- Create the subdirectories
applibs
and dbglibs
:
% mkdir applibs dbglibs
- Unpackage the tar files. Be sure to use the
tar s
option to strip off
any leading slashes from pathnames during extraction:
% tar xfvs mybug.tar
- Move the shared objects (that were originally in
/usr/shlib
and are now in usr/shlib
) into applibs
:
% mv usr/shlib/* applibs
If the tar xfvs
output in step 9 moved shared objects into other
directories, move them into applibs
as well.
- Make
libpthreaddebug.so
exist in the dbglibs
directory,
for example, by linking it to the file in the applibs
directory:
% ln -s ../applibs/libpthreaddebug.so dbglibs/libpthreaddebug.so
- Set the LADEBUG_COREFILE_LIBRARY_PATH environment variable to the
application subdirectory. This directs the debugger to look for shared objects
(by their base names) in the application subdirectory before trying the system
directories. If the POSIX Threads Library is involved, set the LD_LIBRARY_PATH environment
variable to the debugger subdirectory so that the debugger will use the correct
libpthreaddebug.so.
% env LADEBUG_COREFILE_LIBRARY_PATH=applibs \
LD_LIBRARY_PATH=dbglibs \
ladebug a.out core
- Determine that the shared objects are in the
applibs
subdirectory
rather than in /usr/shlib/
:
(ladebug) listobj
For an alternative method when the debugger cannot be run on the original
system, see the corefile_listobj.c
example.
- Debug as usual:
(ladebug)
The following is a complete example, from core creation, through
transporting and core file debugging:
- Create the core file:
% a.out -segv
Segmentation fault (core dumped)
- Determine the shared objects using the debugger on the original system:
% ladebug a.out core
Welcome to the Ladebug Debugger Version n
------------------
object file name: a.out
core file name: core
Reading symbolic information ...done
Core file produced from executable a.out
Thread 0x5 terminated at PC 0x3ff8058b448 by signal SEGV
(ladebug) listobj
section Start Addr End Addr
------------------------------------------------------------------------------
a.out
.text 0x120000000 0x120003fff
.data 0x140000000 0x140001fff
/usr/shlib/libpthread.so
.text 0x3ff80550000 0x3ff8058bfff
.data 0x3ffc0180000 0x3ffc018ffff
.bss 0x3ffc0190000 0x3ffc01901af
/usr/shlib/libmach.so
.text 0x3ff80530000 0x3ff8053ffff
.data 0x3ffc0170000 0x3ffc0173fff
/usr/shlib/libexc.so
.text 0x3ff807b0000 0x3ff807b5fff
.data 0x3ffc0210000 0x3ffc0211fff
/usr/shlib/libc.so
.text 0x3ff80080000 0x3ff8019ffff
.data 0x3ffc0080000 0x3ffc0093fff
.bss 0x3ffc0094000 0x3ffc00a040f
(ladebug) quit
- Cut, paste, and edit the result into a list of
file names. Note that
libpthread.so
is included, so add
/usr/shlib/libpthreaddebug.so
to the list.
-
Create a tar file:
% tar cfv mybug.tar a.out core \
/usr/shlib/libpthread.so /usr/shlib/libmach.so \
/usr/shlib/libexc.so /usr/shlib/libc.so \
/usr/shlib/libpthreaddebug.so
a a.out 128 Blocks
a core 2128 Blocks
a /usr/shlib/libpthread.so 928 Blocks
a /usr/shlib/libmach.so 208 Blocks
a /usr/shlib/libexc.so 96 Blocks
a /usr/shlib/libc.so symbolic link to ../../shlib/libc.so
a /usr/shlib/libpthreaddebug.so 592 Blocks
Note that libc.so
is a symbolic link. Therefore, use
the tar
h
option to force tar
to follow symbolic
links as if they were normal files or directories:
% tar hcfv mybug.tar a.out core \
/usr/shlib/libpthread.so /usr/shlib/libmach.so \
/usr/shlib/libexc.so /usr/shlib/libc.so \
/usr/shlib/libpthreaddebug.so
a a.out 128 Blocks
a core 2128 Blocks
a /usr/shlib/libpthread.so 928 Blocks
a /usr/shlib/libmach.so 208 Blocks
a /usr/shlib/libexc.so 96 Blocks
a /usr/shlib/libc.so 3193 Blocks
a /usr/shlib/libpthreaddebug.so 592 Blocks
Now you have a package that you can transport.
-
On the current system, create a directory for debugging, move to that
directory, and get the package:
% mkdir mybug
% cd mybug
% mv <wherever>/mybug.tar .
-
Create the necessary subdirectories and unpackage the
tar
file
using the s
option:
% mkdir applibs dbglibs
% tar xfvs mybug.tar
blocksize = 256
x a.out, 65536 bytes, 128 tape blocks
x core, 1089536 bytes, 2128 tape blocks
x usr/shlib/libpthread.so, 475136 bytes, 928 tape blocks
x usr/shlib/libmach.so, 106496 bytes, 208 tape blocks
x usr/shlib/libexc.so, 49152 bytes, 96 tape blocks
x usr/shlib/libc.so, 1634400 bytes, 3193 tape blocks
x usr/shlib/libpthreaddebug.so, 303104 bytes, 592 tape blocks
-
Move the original shared objects into
applibs
, and make
libpthreaddebug.so
exist in the dbglibs
directory,
for example, by linking it to the file in the applibs
directory:
% mv usr/shlib/* applibs
% ln -s ../applibs/libpthreaddebug.so dbglibs/libpthreaddebug.so
In this example, all shared objects were in usr/shlib/
, so no other
moving is needed.
- Observe the file system:
% ls -lR
total 4904
-rwxr-xr-x 1 user1 ladebug 65536 Sep 17 11:20 a.out*
drwxrwxr-x 2 user1 ladebug 8192 Sep 17 11:36 applibs/
-rw------- 1 user1 ladebug 1089536 Sep 17 11:21 core
drwxrwxr-x 2 user1 ladebug 8192 Sep 17 11:24 dbglibs/
-rw-rw-r-- 1 user1 ladebug 3737600 Sep 17 11:23 mybug.tar
drwxrwxr-x 3 user1 ladebug 8192 Sep 17 11:36 usr/
./applibs:
total 2632
-rw-r--r-- 1 user1 ladebug 1634400 Dec 7 1998 libc.so
-rw-r--r-- 1 user1 ladebug 49152 Jun 26 1998 libexc.so
-rw-r--r-- 1 user1 ladebug 106496 Dec 29 1997 libmach.so
-rw-r--r-- 1 user1 ladebug 475136 Dec 7 1998 libpthread.so
-rw-r--r-- 1 user1 ladebug 303104 Dec 7 1998 libpthreaddebug.so
./dbglibs:
total 0
lrwxrwxrwx 1 user1 ladebug 29 Sep 17 11:24 libpthreaddebug.so@ -> ../applibs/libpthreaddebug.so
./usr:
total 8
drwxrwxr-x 2 user1 ladebug 8192 Sep 17 11:36 shlib/
./usr/shlib:
total 0
%
If other files need to be moved into applibs
, do
that as well and then re-observe the file system. In this example, there
are none.
-
Now set the environment variables as indicated:
% env LADEBUG_COREFILE_LIBRARY_PATH=applibs \
LD_LIBRARY_PATH=dbglibs \
ladebug a.out core
Welcome to the Ladebug Debugger Version n
------------------
object file name: a.out
core file name: core
Reading symbolic information ...done
Core file produced from executable a.out
Thread 0x5 terminated at PC 0x3ff8058b448 by signal SEGV
- Issue the
listobj
command to ensure the application libraries
are coming from applibs/
. Find any that are not,
either from the original system, or unpacked from the tar file but
not yet moved into applibs
.
(ladebug) listobj
section Start Addr End Addr
------------------------------------------------------------------------------
a.out
.text 0x120000000 0x120003fff
.data 0x140000000 0x140001fff
applibs/libpthread.so
.text 0x3ff80550000 0x3ff8058bfff
.data 0x3ffc0180000 0x3ffc018ffff
.bss 0x3ffc0190000 0x3ffc01901af
applibs/libmach.so
.text 0x3ff80530000 0x3ff8053ffff
.data 0x3ffc0170000 0x3ffc0173fff
applibs/libexc.so
.text 0x3ff807b0000 0x3ff807b5fff
.data 0x3ffc0210000 0x3ffc0211fff
applibs/libc.so
.text 0x3ff80080000 0x3ff8019ffff
.data 0x3ffc0080000 0x3ffc0093fff
.bss 0x3ffc0094000 0x3ffc00a040f
- Now debug as usual:
(ladebug) where
>0 0x3ff8058b448 in nxm_thread_kill(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
#1 0x3ff80578c58 in pthread_kill(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
#2 0x3ff8056cd34 in UnknownProcedure3FromFile69(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
#3 0x3ff807b22d8 in UnknownProcedure4FromFile1(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
#4 0x3ff807b3824 in UnknownProcedure17FromFile1(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
#5 0x3ff807b3864 in exc_unwind(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
#6 0x3ff807b3af0 in exc_raise_signal_exception(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
#7 0x3ff8057a328 in UnknownProcedure6FromFile80(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
#8 0x3ff800d6a30 in __sigtramp(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libc.so
#9 0x120001d94 in mandel_val(cr=0.01, ci=0.16, nmin=0, nmax=255) "mb_pi.c":62
#10 0x12000274c in smp_fill_in_data(raw_mthread=0x11fffe998) "mb_pi.c":338
#11 0x3ff80582068 in thdBase(0x0, 0x2, 0x0, 0x0, 0xff, 0x1) in applibs/libpthread.so
(ladebug) quit
%
16.5 Quick Reference for Transporting a Core File
This section contains a quick reference for transporting a core file.
First, do the following steps on the original system:
- %
ladebug a.out core
(ladebug) listobj; quit
- Cut, paste, and edit into list of file names.
- Add
/usr/shlib/libpthreaddebug.so
, if libpthread.so
.
- %
tar cfvh mybug.tar a.out core <shlibs>
Next, do the following steps on the current system:
- %
mkdir mybug
- %
cd mybug
- %
mv mybug.tar
- %
mkdir applibs dbglibs
- %
tar sfvc mybug.tar
- %
mv usr/shlib/* applibs
- %
ln -s ../applibs/libptheaddebug.so dbglib/libptheaddebug.so
The ../applibs is not a typo. Think of it as:
- %
cd dbglibs
- %
ln -s ../applibs/libpthreaddebug.so libpthreaddebug.so
- %
cd ..
- %
env LADEBUG_COREFILE_LIBRARY_PATH=applibs \
LD_LIBRARY_PATH=dbglibs \
ladebug a.out core
- (ladebug)
listobj
- (ladebug)
This chapter discusses kernel debugging and contains information for each type of
kernel debugging that you may use.
This chapter contains the following sections:
Local kernel debugging is useful for examining the
state of the running kernel or any of its modules or components. If a process
is hung, a system administrator might want to debug the kernel to find out what
the process is caught on. The debugger has no ability to stop a local kernel,
because that will stop the debugger, too.
When a local kernel crashes, systems personnel
typically want to know why. The kernel has been designed to save a copy of its
memory state to a core file just before crashing. A copy of the kernel that
crashed is also saved. These two files can be loaded into the debugger, and the
state of the kernel just prior to the crash can be examined, to determine what
went wrong.
Remote kernel debugging is useful for systems
engineers who are building and testing kernels and who need to have more control
over the kernel. Breakpoints can be set in a kernel that is debugged remotely,
which allow it to be stopped and examined more closely. Remote kernel debugging
requires at least two machines, with the debugger running on one and the kernel
to be tested on the other.
The kernel is typically owned by root, so you may need to be the superuser
(root
login) to examine either the running system or crash dumps.
Whether or not you need to be the superuser to debug a kernel directly depends
on the directory and file protections of the files you attempt to examine.
Ideally, the kernel should be compiled without full optimization and without
stripping the kernel of its symbol table information, so that the debugger can
provide you with the most information in the most friendly manner. However,
most of the time this is not the case, and you are working with a
stripped-down, highly optimized kernel. In these cases, the debugger is
limited in what it can display. Information relating to source files is not
available, and often function parameters and other variables are "optimized
away."
The kernel is a complex piece of software. It contains kernel processes
denoted by a process ID (pid). Some of these processes relate to user processes, and
some are kernel specific.
NOTE: Because the debugger supports multiprocessing as
well as kernel debugging, you need to be able to distinguish between processes
that the debugger is managing, and processes being managed within the
kernel.
Because the debugger command for managing multiple processes is
process
, this
chapter refers to a process being debugged as a process, and refers
to a process within the kernel being debugged as a pid.
The debugger maintains two extra debugger variables when debugging a kernel:
$pid
and $tid
. These variables assist
you in establishing and changing the user context while debugging the
kernel.
The $pid
variable contains the current pid that you are
examining. You can switch the context to another pid within the kernel by
setting $pid
to the desired value. The debugger variable
$curprocess
, which is read-only,
does not change. The value in the $curprocess
variable is how the
debugger refers to the kernel process as a whole. Typically, this value is
the actual kernel process listed as [kernel idle]
with the
ps
system command (or Ladebug
kps
command), but
not always. In any case, the $pid
variable controls the pid
that you are
examining within the kernel.
The $tid
variable contains the current thread ID that you
are examining. It, too, can be used to switch the user context to a different
thread (it simply calls the
thread
command with its new value).
The debugger also supplies a kps
command, which displays all
the pids in the kernel. This command is not available for
remote kernel debugging.
kernel_debugging_command
: kps
For example, if you wanted to examine the stack of the kloadsrv
daemon, you would first find out its pid:
(ladebug) kps
00000 kernel idle
00001 init
00003 kloadsrv
00020 update
02082 dtexec
02092 dtterm
02093 csh
...
Then set the $pid
accordingly and enter the
where
command:
(ladebug) set $pid = 3
(ladebug) where
>0 0xfffffc00002b3a10 in thread_block()
From within the debugger, you can use the
patch
command to
correct bad data or instructions in an executable disk file. You can patch the text,
initialized data, or read-only data areas. You cannot patch the bss
segment because it does not exist in disk files. For example:
(ladebug) patch @foo = 20
You can specify addresses in the KSEG segment by prefixing the
hexadecimal offset of the address with 0k
; the debugger adds the
KSEG base address to any such hexadecimal constant. For example,
the constant 0k2400
is converted by the debugger into the
actual address for the location at offset 0x2400 into the KSEG segment.
Tru64 UNIX provides libdpi
(Debugger Process Interface library) for interacting with the
kernel. The archive form (libdpi.a
) is built into the debugger so kernel developers
can debug the Tru64 UNIX kernel.
However, kernel developers can also access libdpi
as an external shared object. This is useful
for the following tasks:
- Debugging a new kernel that requires a new
libdpi
- Debugging across different platforms and accessing a
libdpi
that is
compatible with the target kernel
If you need to use a libdpi
that is different from the one
linked into the debugger, contact Ladebug engineering for instructions
on how to create an appropriate shared object version and how to use it.
When you have a problem with a process, you can debug the running kernel or
examine the values assigned to system parameters. (It is generally recommended
that you avoid modifying the value of the parameters, which can cause problems
with the kernel.)
Invoke the debugger with the following command:
# ladebug -k /vmunix /dev/mem
The -k
flag maps virtual to physical addresses to
enable local kernel debugging. The /vmunix
and
/dev/mem
parameters cause the debugger to operate
on the running kernel (the /dev/mem
parameter is
optional when debugging the live kernel, the debugger knows to look there for
the kernel address space).
Now you can use debugger commands to display information such as the current
list of pids (kps
), and trace the execution of processes. Note that
the debugger motion commands such as cont, next, rerun, run, step,
return,
and stop
are not
available, nor can you change values in registers when you do local kernel
debugging (stopping the kernel would also stop the debugger).
If your system panics or crashes, you can often find the cause by using the
debugger to analyze a crash dump. Keep in mind that the debugger is only one
useful tool for determining the cause of a crash. Other tools and
techniques are available to system personnel to aid in analyzing crash
dumps. Some of them are mentioned briefly
here.
NOTE: You cannot perform crash dump analysis remotely with this
debugger.
The operating system can crash in the following ways:
-
Hardware trap: A hardware problem often results in the kernel
trap()
function being invoked.
-
Software panic: A software panic, resulting from a software failure,
calls the kernel
panic()
function.
-
Hung system: When the system hangs, you can force the creation of
dump files.
If the system crashes because of a hardware fault or an unrecoverable software
state, a dump function is invoked. The dump function copies the core memory
into the primary default swap disk area as specified by the
/etc/fstab
file structure table and the
/sbin/swapdefault
file. At system reboot time, the information is
copied into a file, called a crash dump file. Crash dump files are either
partial (the default) or full. See Compaq TRU64 UNIX Kernel
Debugging for more information.
You can analyze the crash dump file to determine what caused the crash. For
example, if a hardware trap occurred, you can examine variables, such as
savedefp
, the program counter ($pc
), and the stack
pointer ($sp
), to help you determine why the crash occurred. If a
software panic caused the crash, you can use the debugger to examine the crash
dump and use the uerf
utility to examine the error log. Using these
tools, you can determine which function called the panic()
routine.
When you examine crash dump files, there is no one way to determine the cause of a
system crash. However, the following guidelines can help you identify the
events that led to the crash:
-
Gather some facts about the system (for example, operating system type,
version number, revision level, and hardware conGuration).
-
Look at the panic string, if one exists. This string is contained in the
preserved message buffer,
pmsgbuf
, and in the
panicstr
global variable.
-
Locate the thread executing at the time of the crash. (Use the
where
command.) Most likely, this thread will contain the
events that led to the panic.
-
Determine whether you can fix the problem. If the system crashed because of
lack of resources (for example, swap space), you can probably eliminate the
problem by adding more of that resource. If the problem is with the
software, you may need to contact Compaq Customer Support.
For more information and for examples, see Compaq TRU64 UNIX Kernel
Debugging. This manual contains detailed information on the following
topics related to crash dump analysis:
-
The
crashdc
utility
-
Managing crash dump file creation
-
Saving dumps to a file system
-
Selecting full or partial crash dump files
-
Determining crash dump partition size and file system space
-
Procedures (according to hardware platform) for creating dumps of a hung
system
You can find Compaq Tru64 UNIX crash dump files in /var/adm/crash
.
There you will find a copy of the kernel that crashed
(vmunix.number
) and the core memory file
(vmcore.number
or
vmzcore.number
, depending on the operating system
version). The number appended to these files distinguishes crashes, with the
highest number denoting the most recent crash. These files are owned by
root
, so you must have root permissions to access them.
To invoke the debugger on a kernel crash dump numbered 0 (zero), enter the following
command:
# ladebug -k vmunix.0 vmzcore.0
On startup, the debugger analyzes the core file to determine the final PC
address of the crash, and outputs that information. You can use the debugger to
inspect kernel data structures, global variables (such as the panic string), and
kernel thread stacks, in order to determine why the kernel crashed.
If you are going to view the stack using the
where
, up
,
down
, and dump
commands, you may want to set the control variable
$stackargs
to 0 (zero) to suppress
the output of argument values, The kernel is typically compiled for high
performance, which means those argument values have been optimized away. If
the debugger is set to look for those arguments and cannot find them, it
notifies you with error messages, causing the stack output to be full of
repetitious warnings.
When debugging a kernel memory fault, verify that the core memory has not been
corrupted; otherwise, the debugger may present erroneous information. One quick
way to check this is by comparing the global panic string with the panic string
in the machine_slot
structure of the machine that caused the
crash. Select the appropriate machine_slot
structure name depending
on your operating system version, as follows:
-
For Compaq Tru64 UNIX Version 4.n systems, use
machine_slot[paniccpu]
.
-
For Compaq Tru64 UNIX Version 5.n systems, use
processor_ptr[paniccpu].m
.
If the strings are different, the core memory file may be corrupted. The
following example shows how to compare these strings:
# ladebug -k vmunix.0 vmzcore.0
Welcome to the Ladebug Debugger Version n
------------------
object file name: vmunix.0
core file name: vmzcore.0
Reading symbolic information ...done
Thread terminated at PC 0xfffffc0000457a48
done
(ladebug) print panicstr
0xfffffc0000640970="kernel memory fault" <== memory fault
(ladebug) print utsname
struct utsname {
sysname = "OSF1";
nodename = "";
release = "X5.1"; <== 5.n system
version = "730";
machine = "alpha";
}
(ladebug) print processor_ptr[paniccpu].m
struct machine_slot {
is_cpu = 1;
cpu_type = 15;
cpu_subtype = 22;
running = 1;
cpu_ticks = [0] = 0,[1] = 0,[2] = 0,[3] = 0,[4] = 0;
clock_freq = 1200;
error_restart = 0;
cpu_panicstr = 0xfffffc0000640970="kernel memory fault"; <== strings match
cpu_panic_thread = 0xfffffc0001c51180;
}
When you debug your code by working with a crash dump file, you can examine the
exception frame using the debugger. The variable savedefp
contains
the location of the exception frame. (No exception frames are created when you
force a system to dump.) Refer to the header file
/usr/include/machine/reg.h
to determine where registers are stored
in the exception frame. The following example shows an exception frame:
(ladebug) print savedefp/33X
ffffffff9618d940: 0000000000000000 fffffc000046f888
ffffffff9618d950: ffffffff86329ed0 0000000079cd612f
.
.
.
ffffffff9618da30: 0000000000901402 0000000000001001
ffffffff9618da40: 0000000000002000
You can use the debugger to extract the preserved message buffer from a running
system or use dump files to display system messages logged by the kernel. For
example:
(ladebug) print *pmsgbuf
struct msgbuf {
msg_magic = 405601;
msg_bufx = 1851;
msg_bufr = 1343;
msg_bufc = "Alpha boot: available memory from 0x6ca000 to 0x3f16000\nTru64 UNIX V.n (Rev. 564); Fri Jul 11 11:25:29 EDT 1997 \nphysical m";
}
The print
command is regulated in length by the
$maxstrlen
debugger variable. Also,
the print
command does not expand the new line character. To see the
full message string as formatted text, use the following command:
(ladebug) printf "%s",pmsgbuf->msg_bufc
The crashdc
utility collects critical data from operating system
crash dump files or from a running kernel. You can use the data it collects to
analyze the cause of a system crash. The crashdc
utility uses
existing system tools and utilities to extract information from crash dumps. The
information garnered from crash dump files or from the running kernel includes
the hardware and software configuration, current processes, the panic string (if
any), and swap information.
See Compaq TRU64 UNIX Kernel Debugging and
crashdc
(8) for more information.
For remote kernel debugging, use the Ladebug debugger in conjunction with
the kdebug
debugger, which is a tool for executing, testing, and
debugging test kernels.
Used alone, kdebug
has its own syntax and commands, and allows
local non-symbolic debugging of a running kernel across a serial line. See
kdebug
(8) for information about kdebug
local
kernel debugging.
You use Ladebug commands to start and stop kernel execution, examine variable
and register values, and perform other debugging tasks, just as you would when
debugging user-space programs. The kdebug
debugger, not the Ladebug
debugger, performs the actual reads and writes to registers, memory, and the
image itself (for example, when breakpoints are set).
The kernel code to be debugged runs on a test system. The Ladebug debugger runs
on a remote build system and communicates with the kernel code over a serial
communication line or through a gateway system.
You use a gateway system when you cannot physically connect the test and build
systems. Connect the build system to the gateway system over a network. Connect the
gateway system to the test system by a serial communication line.
The following diagram shows the physical connection of the test and build
systems (with no gateway):
Build system Serial line Test system
(with the Ladebug <-------------------------> (kernel code here)
debugger)
The following diagram shows the connections when you use a gateway system:
Build system Network Gateway Serial line Test system
(with the Ladebug <-----------> system <-------------> (kernel code
debugger) with here)
kdebug
daemon
The serial line provides a physical connection between communication ports
on two systems; it consists of a BC16E cable and two H8571-J DECconnect
Passive Adapters:
-
The BC16E cable is a 6-conductor, cross-wired cable terminated on each end
by a Modified Modular Plug (MMP).
-
Each H8571-J DECconnect Passive Adapter is a straight-wired passive adapter
consisting of a 9-pin D-Sub female (DB9) on one end and a Modified Modular
Jack (MMJ) on the other. This provides a means for connecting the MMP at
each end of the cable to one of the 9-pin EIA232 PC-compatible male
communication ports on each of the systems.
The test system always uses the same communication port for kdebug
debugger input and output:
-
For a DEC 3000 family workstation operating as the test system, this device
is always the RS232C port on the back of the system. By default, this line
is identified as
/dev/tty00
at installation time.
-
For a Compaq AlphaStationTM or Compaq
AlphaServerTM system with an
ace
console serial interface operating as the test system, this
device is always the serial communications port 2 identified as
/dev/tty01
.
The build or gateway system, whichever is at the other end of the serial line
from the test system, uses /etc/remote
to specify the device to use
for kdebug
debugger input and output. Serial communication ports 1
and 2 correspond to device names /dev/tty00
and
/dev/tty01
, respectively:
-
For a DEC 3000 family workstation operating as the build or gateway system,
this device is always the RS232C port on the back of the system. By default,
this line is identified as
/dev/tty00
at installation time.
-
For an AlphaStation or AlphaServer system with an ace console serial
interface operating as the build or gateway system, this device is the line
identified by the name
kdebug
in /etc/remote
. By
default, this device is the serial communications port 1 identified as
/dev/tty00
, but you can change it if desired by modifying
/etc/remote
.
The following line in /etc/remote
defines /dev/tty00
as the device to use for kdebug
debugger input and output:
kdebug:dv=/dev/tty00:br#9600:pa=none:
For AlphaStation and AlphaServer systems, it is possible to change this
definition to /dev/tty01
so the same serial port can be used for
remote kernel debugging whether the system is used as a build, gateway, or test
system:
kdebug:dv=/dev/tty01:br#9600:pa=none:
The first field, kdebug
, is a label and has no significance except
as an identifier, and "kdebug" is the default name for the serial line used by
the debugger for kdebug
debugger input and output.
Using a Second Serial Line for Test System Console Emulation
For AlphaStation and AlphaServer systems, it is also possible to redirect the
test system console input and output to the build or gateway system at the other
end of the serial line. This requires a second serial line connected between the
communications ports of the build or gateway system and the test system that are
not used for kdebug debugger input and output.
To configure a second serial line, follow these steps:
-
When the line has been connected, enter console mode on the test system by
shutting it down; console mode is recognizable by the >>> prompt. At the
prompt, enter the following command:
>>> set console serial
This redirects all console input and output to serial communication port 1
(/dev/tty00
).
- On the build or gateway system, modify the
/etc/remote
file to
define this second line. For example, in order to change the serial line used
for kdebug
debugger input and output to /dev/tty01
and
create a useful label for /dev/tty00
to use for the test system
console input and output, replace the original definition for
kdebug
in /etc/remote
on the build or gateway system
with the following:
kdebug:dv=/dev/tty01:br#9600:pa=none:
tstsys:dv=/dev/tty00:br#9600:pa=none:
- On the build or gateway system (in a window separate from the debugger
window if on the build system), enter the following command:
% tip tstsys
You can use this separate window as the console for the test system.
- When
finished, return to the console mode on the test system and enter the following
command in the separate console window on the build or gateway system:
>>> set console graphics
- Exit out of
tip
on the build or gateway system by entering a
tilde and a period (˜.
).
The test, build, and (if used) gateway systems must meet the following
requirements for kdebug
:
-
Test system: Must be running Version 2.0 or higher of the Tru64 UNIX
operating system, must have the Kernel Debugging Tools subset loaded, and
must have the Kernel Breakpoint Debugger kernel option configured.
-
Build system: Must be running Version 3.2 or higher of the Tru64 UNIX
operating system. Also, this system must contain a copy of the kernel code
you are testing and, preferably, the source used to build that kernel code.
-
Gateway system: Must be running Version 2.0 or higher of the Tru64
UNIX operating system, and must have the Kernel Debugging Tools subset
loaded.
You can verify the status of each of the system requirements by the using the
following commands:
-
Tru64 operating system version: Enter the command:
% uname -a
-
Kernel Debugging Tools subset: Enter the command:
% setld -i | grep -i kernel | grep installed
-
Kernel Breakpoint Debugger kernel option: There are two methods to
verify this option:
-
Enter the command:
% /sbin/sysconfig -s kdebug
This should indicate that kdebug
is loaded and configured.
-
Examine configuration file contents:
Look for a file in /sys/config
with the same name as the
node in question, except in all uppercase characters. For example, if
the name of the node is "mine", look for a file named
/sys/config/MINE
and search it for a line containing
"options KDEBUG". If present, it means the Kernel Breakpoint Debugger
kernel option has been configured.
To use the kdebug
debugger, do the following:
-
Attach the test system and the build system or the test system and the
gateway system. See your hardware documentation for information about
connecting systems to serial lines and networks.
-
Configure the kernel to be debugged with the configuration file option
OPTIONS KDEBUG. If you are debugging the installed kernel, you can do this
by choosing KERNEL BREAKPOINT DEBUGGING from the Kernel Options menu.
Recompile kernel files, if necessary. By default, the kernel is compiled
with only partial debugging information, occasionally causing the debugger
to display erroneous arguments or mismatched source lines. To correct this,
recompile selected source files specifying the CDEBUGOPTS=-g
argument.
Copy the kernel to be tested to /vmunix
on the test system.
Retain an exact copy of this image on the build system.
Install the Product Authorization Key (PAK) for the Developers' kit
(OSF-DEV) if it is not already installed. For information about installing
PAKs, see the Compaq Tru64 UNIX Software License Management manual.
Determine the debugger variable settings or command-line options you will
use. On the build system, add the following lines to your
.dbxinit
file if you need to override the default values (and
you choose not to use the corresponding options).
Alternatively, you can use the following lines within the debugger session, at the
(ladebug)
prompt:
(ladebug) set $kdebug_host="<name_of_your_gateway_system>"
(ladebug) set $kdebug_line="<name_of_your_serial_line>"
(ladebug) set $kdebug_dbgtty="<name_of_your_tty>"
(ladebug) set $kdebug_host="decosf"
$kdebug_line
specifies the serial line to use as defined in
the /etc/remote
file of the build system (or the gateway
system, if one is being used). By default, $kdebug_line
is
set to kdebug
. For example:
(ladebug) set $kdebug_line="kdebug"
$kdebug_dbgtty
sets the terminal on the gateway system to
display the communication between the build and test systems, which is
useful in debugging your setup. To determine the terminal name to supply
to the $kdebug_dbgtty
variable, enter the tty
command in the desired window on the gateway system. By default,
$kdebug_dbgtty
is null. For example:
(ladebug) set $kdebug_dbgtty="/dev/ttyp2"
Instead of using debugger variables, you can specify any of the following
options on the ladebug
command line:
The -rn
option specifies the node or address of the gateway
system, and can be used instead of $kdebug_host
.
The -line
option specifies the serial line, and can be used
instead of $kdebug_line
.
The -tty
option specifies the terminal name, and can be
used instead of $kdebug_dbgtty
.
The preceding three options require the -remote
option or its
alternative, the -rp kdebug
option, for example:
# ladebug -remote -rn "decosf" -line "kdebug" -tty "/dev/ttyp2" /usr/test/vmunix
The -rn
, -line
, and -tty
options on
this command line produce the same result as the preceding
set
command examples. This example assumes a
copy of the test system's vmunix
is in /usr/test
on the build system.
The variables you set in your .dbxinit
file override any
options you use on the ladebug
command line. In your debugging
session, you can still override the .dbxinit
variable settings
by using the set
command at the
(ladebug)
prompt, prior to issuing the
run
command.
If you are debugging on a symmetric multiprocessing (SMP) system , set the
lockmode
system attribute to 4, as shown:
# sysconfig -r lockmode = 4
Setting this system attribute makes debugging on an SMP system easier.
When the setup is complete, start the debugger as follows:
Invoke the Ladebug debugger on the build system, supplying the pathname of
the copy of the test kernel that resides on the build system. Set a
breakpoint and start running the Ladebug debugger as follows (assuming that
vmunix
resides in the /usr/test
directory):
# ladebug -remote /usr/test/vmunix
...
(ladebug) stop in panic
[2] stop in panic
(ladebug) stop in ttyretype
[3] stop in ttyretype
Because you cannot use Ctrl/C as an interrupt, set at least one breakpoint
if you want the debugger to gain control of kernel execution. You can set a
breakpoint any time after the execution of the
kdebug_bootstrap()
routine. Setting a breakpoint prior to the
execution of this routine can result in unpredictable behavior. Setting a
breakpoint in panic
enables regaining control after a panic, and
setting a breakpoint in ttyretype
enables returning control to
the debugger whenever Ctrl/R is entered at the console.
NOTE: Pressing Ctrl/C causes the remote debugger to exit,
not to interrupt as it does during local debugging.
Halt the test system:
# shutdown -h now
At the console prompt, set the boot_osflags
console variable to
contain the value k
(the default is usually the value
a
). For example:
>>> set boot_osflags k
Alternatively, you can enter the following command:
>>> boot -flags k
You can abbreviate the boot
command to b
and the
-flags
option to -fl
. The
boot
command without the -flags
option boots the
machine using the current value for boot_osflags
; with the
-flags
option, it uses the value specified in the option and
does not change the value of the boot_osflags
console variable.
Other useful commands from the console prompt include show
,
which lists the values of console variables, and help
, which
provides information about other commands. For more information about the
boot_osflags
values, see the Compaq Tru64 UNIX
System Administration manual and the Compaq Tru64 UNIX
Installation Guide.
With the Ladebug debugger started and waiting at a (ladebug)
prompt with breakpoints already set and the test system waiting at the
console prompt (>>>), start both the Ladebug debugger and test system kernel
executing. You can start them in either order as follows:
If you set breakpoints in code that is executed on an SMP system, the
breakpoints are handled serially. When a breakpoint is encountered on a
particular CPU, the state of all the other processors in the system is saved and
those processors spin, similar to how execution stops when a simple lock is
obtained on a particular CPU.
When the breakpoint is dismissed (for example, because you entered a
step
or cont
command
to the debugger), processing resumes on all processors.
If you have completed the kdebug
setup and it fails to work, read
this section for help:
Be sure the serial line is attached properly. Use the
tip
command to test the connection. Log on to
the build system (or the gateway system if one is being used) as root and
enter the following command:
# tip kdebug
If the command does not return the message "connected," another process,
such as a print daemon, might be using the serial line port that you have
dedicated to the kdebug
debugger. To remedy this condition, do
the following:
Check the /etc/inittab
file to see if any processes are
using that line. If so, disable these lines until you finish with the
kdebug
session. See inittab
(4) for information
on disabling lines.
Use the ps
command to see if any processes
are using the line. For example, if you are using the
/dev/tty00
serial port for your kdebug
session, check for other processes using the serial line as follows:
# ps agxt00
If a process is using tty00
, kill that process.
Determine whether any unused kdebugd
gateway daemons are
running:
# ps agx | grep kdebugd
If one is running, kill the process.
-
If the test system boots to single user or beyond,
kdebug
has not been configured into the kernel as specified in the section Getting Ready to Use the kdebug Debugger. Ensure
that the boot_osflags
console environment variable
specifies the k
flag and try booting the system again:
>>> set boot_osflags k
>>> boot
- Be sure you defined the Ladebug debugger variables in your
.dbxinit
file correctly, or specify them correctly on the
command line. Determine the pseudoterminal line from which you ran
tip
by issuing the /usr/bin/tty
command.
For example:
# /usr/bin/tty
/dev/ttyp2
The example shows that you are using pseudoterminal
/dev/ttyp2
. Edit your $HOME/.dbxinit
file on
the build system as follows:
Set the $kdebug_dbgtty
variable to
/dev/ttyp2
with this command:
(ladebug) set $kdebug_dbgtty="/dev/ttyp2"
Set the variable $kdebug_host
to the host name of the
system from which you entered the tip
command. For example,
if the host name is decosf
, the entry in the
.dbxinit
file should be:
(ladebug) set $kdebug_host="decosf"
Remove any settings of the $kdebug_line
variable:
(ladebug) set $kdebug_line=""
-
Start the Ladebug debugger on the build system. You should see informational
messages on the pseudoterminal line,
/dev/ttyp2
, which
kdebug
is starting.
If you are using a gateway system, ensure that the inetd
daemon
is running on the gateway system. Also, check the TCP/IP connection between
the build and gateway system using one of the following commands:
rlogin
, rsh
, or
rcp
.
The debugger lets you debug your programs at the machine-code level as
well as at the source-code level. Using debugger commands, you can examine and
edit values in memory, print the values of all machine registers, and step
through program execution one machine instruction at a time.
Only those users familiar with machine-language programming and
executable file code structure will find low-level debugging useful.
This chapter contains the following sections:
You can examine the value contained at an address in memory as follows:
- The examine commands
(
/
and ?
)
display the values stored in memory.
- The
print
command,
with the appropriate pointer arithmetic, prints the value contained at the
address in decimal.
- The
printregs
command
prints the values of all machine-level registers.
In addition to examining memory, you can also search memory in 32-bit
and 64-bit chunks.
You can use the examine commands
(/
and ?
) to print the
value contained at the address in one
of a number of formats (decimal, octal, hexadecimal, and so on). See
Memory Display Commands for more information.
The debugger also maintains the
$readtextfile
debugger variable, which allows you to view the
data from the text section of the executable directly
from the binary file, rather than reading it from memory. You can use
this variable to speed up the reading of instructions during kernel
debugging, however, remember that you are reading from the file and
not from the live image, which could be different.
You can use C and C++ pointer-type conversions to display the contents of a
single address in decimal. Using the print
command, the syntax is as
follows:
print *(int *)(address)
Using the same pointer arithmetic, you can use the assign
command to alter the
contents of a single address. Use the following syntax:
assign *(int *)(address) = value
The following example shows how to use pointer arithmetic to examine and change
the contents of a single address:
(ladebug) print *(int*)(0x10000000)
4198916
(ladebug) assign *(int*)(0x10000000) = 4194744
(ladebug) print *(int*)(0x10000000)
4194744
(ladebug)
The printregs
command prints the values of all machine-level registers.
The registers displayed by the debugger are machine dependent. The values are
in decimal or hexadecimal, depending on the value of the $hexints
variable (the default is 0, decimal). The register aliases are shown; for
example, $r1 [$t0]
. See the
printregs
command for more information.
The stepi
and nexti
commands let you step through program
execution incrementally, like the step
and next
commands. The
stepi
and nexti
commands execute one machine instruction at a
time, as opposed to one line of source code. The following example shows
stepping at the machine-instruction level:
(ladebug) stop in main
[#1: stop in main ]
(ladebug) run
[1] stopped at [main:4 0x120001180]
4 for (i=1 ; i<3 ; i++) {
(ladebug) stepi
stopped at [main:4 0x120001184] stl t0, 24(sp)
(ladebug) [Return]
stopped at [main:5 0x120001188] ldl a0, 24(sp)
(ladebug) [Return]
stopped at [main:5 0x12000118c] ldq t12, -32664(gp)
(ladebug) [Return]
stopped at [main:5 0x120001190] bsr ra,
(ladebug) [Return]
stopped at [factorial:12 0x120001210] ldah gp, 8192(t12)
(ladebug)
At the machine-instruction level, you can step into, rather than over, a
function's prologue. While within a function prologue, you may find that the stack
trace, variable scope, and parameter list are not correct. Stepping out of the
prologue and into the actual function updates the stack trace and
variable information kept by the debugger.
Single-stepping through function prologues that initialize large local variables
is slow. As a workaround, use the next
command.
Load locked store conditional sequences have special semantics. See the
Alpha Architecture Reference Manual and the Alpha Architecture
Handbook for more information.
The debugger interaction with the target modifies the target
state enough that the store conditional is guaranteed to fail. So that
users can make progress, the debugger normally steps over
the entire sequence as though it were one big instruction.
This allows the application to continue normally.
To debug inside the sequence, while understanding
that this implies failure of the store, set a
breakpoint inside the sequence and continue to the breakpoint.
From there, you will be able to stepi
or
nexti
.
The Ladebug debugger Version 67
supports debugging of parallel applications on the
Compaq Tru64 UNIX operating system. Earlier versions of
Ladebug do not have this capability.
Ladebug supports the following parallel launchers:
This chapter contains the following sections:
The biggest challenge of debugging massively parallel applications is coping
with large quantities of output from debuggers controlling the parallel application's
processes. Ladebug helps you do this by condensing
(aggregating) similar output into groups. Aggregation
is performed by using the
following two strategies:
- Identical output messages are condensed into a single output message. When
a condensed message is displayed, it is prefixed with a range of user process IDs
(not necessarily consecutive) to which this output applies. All processes with
the same output are aggregated into a single and final output message, for
example:
[0-41] Welcome to the Ladebug Debugger Version n
|
Process range
- Outputs that have different hexadecimal digits, but are otherwise
identical, are condensed by aggregating the differing digits into a range, for
example:
[0-41]>2 0x120006d6c in feedback(myid=[0;41],np=42,name=0x11fffe018="mytest") "mytest.c":41
| |
Process range Value range
Another challenge of debugging massively parallel applications is controlling all
processes or subsets of the parallel application's processes from the debugger
in a consistent manner. The Ladebug debugger allows you to control all or a subset
of your processes through a single user interface. At the startup of a parallel
debugging session, Ladebug does the following:
- Detects the topology of your application and attaches a debugger to each of your
application's processes.
- Builds an n-nary tree with the Ladebug debuggers as root and leaves with
special processes called aggregators in the middle (shown in the following diagram).
You can specify the tree's branching factor and the
aggregator time delay.
The root debugger is responsible for starting your parallel application and
serves as your user interface. The aggregators perform output consolidation as
described previously. The leaf debuggers control and query your application
processes.
The branching factor is the factor used to build the n-nary tree and
determine the number of aggregators in the tree. For example, for 16 processes:
- Using a branching factor of 8 creates 3 aggregators
- Using a branching factor of 2 creates 15 aggregators
You can set the value of the $parallel_branchingfactor
variable
from its default value of 8 to a value equal to or greater than 2 in the ladebug initialization
file (.ladebugrc
, and so on).
When you delete $parallel_branchingfactor
from the ladebug
initialization
file, the branching factor used in the startup mechanism is the
default value.
Aggregator delay specifies the time that aggregators wait before they aggregate
and send messages down to the next level when not all of the expected messages
have been received.
You can change the value of the $parallel_aggregatordelay
variable
from its default value of 3000 milliseconds in the ladebug
initialization file
(.ladebugrc
, etc.). See Parallel Debugging Tips for more
information.
When you delete $parallel_aggregatordelay
from the ladebug initialization
file, the aggregator delay used in the startup mechanism is the
default value.
NOTE: You can only change the values that are set for
$parallel_branchingfactor
and
$parallel_aggregatordelay
at startup, in the
.ladebugrc
file. After the program has started up, you cannot change
these values.
To start your parallel application under debugger control, issue
the following commands at the shell (N
represents number of processes):
- If you are using the
prun
launcher on
SC clusters:
% which ladebug
/usr/bin/ladebug
% setenv LADEBUG_HOME /usr/bin/
% which prun
/bin/prun
% ladebug -parallel /bin/prun -n N [prun options]
/full/path/to/your/application
NOTE: Specify a full path both to your prun
launcher and to
your application's executable.
- If you are using the
mpich
public
domain mpirun
launcher:
% which ladebug
/usr/bin/ladebug
% setenv LADEBUG_HOME /usr/bin/
% which mpirun
/bin/mpirun
% mpirun -dbg=ladebug -np N [mpich options] /full/path/to/your/application
NOTE: Specify a full path both to your mpirun
launcher and to
your application's executable. Make sure that there is a file called
mpirun_dbg.ladebug
in the directory in which
mpirun
is located.
- If you are using the
dmpirun
launcher:
% which ladebug
/usr/bin/ladebug
% setenv LADEBUG_HOME /usr/bin/
% which dmpirun
/bin/dmpirun
% ladebug -parallel /bin/dmpirun -np N [dmpirun options]
/full/path/to/your/application
NOTE: Specify a full path both to your dmpirun
launcher and to
your application's executable.
When the debugger starts your parallel application, it detects and attaches to
all of your application's processes. At this point, your application stops before
executing any user code and the debugger displays a prompt.
You can now set any necessary breakpoints and
use the continue
command to continue the execution
of your application.
You can use most Ladebug commands just as you would when debugging a
non-parallel application. Most commands are passed on to the leaf debuggers
and you see aggregated output from them in your user interface. However, there
are a few important exceptions.
The following table shows debugger commands that can be accessed remotely, locally, and both
remotely and locally for parallel debugging; and Ladebug commands that are disabled
for parallel debugging.
Remote |
Local |
Both Remote and
Local |
Disabled |
#
/
?
assign
call
catch
class
cont/conti
delete
delsharedobj
disable
down
dump
enable
examine_address
file
func
goto
history
if
ignore
kill
list
listobj
map/unmap source directory
next/nexti
pop
print
printb
printd
printf
printi
printo
printregs
printt
printx
process
readsharedobj
return
show condition
show mutex
show process
show source directory
show thread
status
step/stepi
stop/stopi
thread
trace/tracei
use/unuse
up
watch
whatis
when/wheni
where/whereis
which |
!/history
alias/unalias
edit
export
help
playback
quit
record/unrecord
source |
export
set/setenv
sh
unset/unsetenv |
attach/detach
kps
load/unload
patch
printenv
rerun
run
snapshot |
Remote means commands will be sent to the leaf debuggers.
Local means that commands are not sent to the leaf debuggers
but are processed by the local Ladebug.
In addition to the commands listed in the table, you can use four other Ladebug
commands to assist parallel debugging:
parallel_debugging_command
:focus_command
|show_process_set_command
|show_aggregated_message_command
|expand_aggregated_message_command
When there are many processes, it can be annoying or impractical to enumerate all
the processes when one needs to focus on specific processes. Therefore,
Ladebug introduces the concept of "process sets" and "process ranges" to let the
user specify a group of processes in a compact form. Moreover, process sets come
with the usual set operations, and both the sets and the ranges can be stored in
debugger variables for manipulation, reference, or inspection at a later time.
A process set is a bracketed list of process ranges separated by commas.
NOTE: Because brackets ([..]) are part of the process set
syntax, this section shows optional syntactic items enclosed in curly braces ({..}).
process_set
: [ ]
| [ process_range {,...} ]
NOTE: The set can be empty.
A process range has the following three forms:
process_range
: *
| expression
| { expression } : { expression }
In the first form, the star (*
) specifies all processes.
You can use the second form as follows:
-
If expression evaluates to, or can be coerced into an integer
p
, then
the range contains the process with pid p
only.
- If expression evaluates to a process range
r
, then the process
range is the same as r
.
You can use the third form to specify a contiguous range of processes.
For example, 10:12
stands for the processes associated with pids
10, 11, and 12.
NOTE: A range whose lower bound is greater than its
upper bound is illegal and will be ignored.
Because both the lower bound and the upper bound are optional, you
can specify ranges as follows:
Example |
Represents |
:5 |
All processes whose pid is no
greater than 5. |
20: |
All processes whose pid is no less than 20. |
NOTE: The process set [:
] is equivalent to
the process set [*].
Like storing other data types supported by the debugger, you can store process sets
and process ranges in debugger variables using the set
command.
For example:
(ladebug) set $set1 = [:7, 10, 15:20, 30:]
(ladebug) print $set1
[:7, 10, 15:20, 30:]
In addition to using the print
command, you can also
use the show process set
command to inspect the process set
stored in a debugger variable. For example:
show_process_set_command
: show process set debugvar_name
| show process set all
| show process set
If you do not specify the set name, or if you use the all
specifier, the
debugger displays all the process sets that are currently stored in debugger variables, as the
continued example shows:
(ladebug) set $set2 = [8:9, 5:2, 22:27]
`5:2' is not a legal process range. Ignored.
(ladebug) show process set $set2
$set2 = [8:9, 22:27]
(ladebug) show process set *
$set1 = [:7, 10, 15:20, 30:]
$set2 = [8:9, 22:27]
You can use the following three operations on process sets:
Operation |
Represents |
Action |
+ |
Set union |
Takes two sets S1 and S2 and returns a set whose
elements are either in S1 or in S2. |
- |
Difference |
Takes two sets S1 and S2 and returns a
set whose elements are in S1 but not in S2. |
unary - |
Negation |
Takes a single set S and
returns the difference of [* ] and S. |
The following example demonstrates these operations:
(ladebug) set $set1 = [:10, 15:18, 20:]
(ladebug) set $set2 = [10:16, 19]
(ladebug) set $set3 = $set1 + $set2
(ladebug) print $set3
[*]
(ladebug) print $set3 - $set2
[:9, 17:18, 20:]
(ladebug) print -$set2
[:9, 17:18, 20:]
You can use the focus
command to change the current
process set, which is the set of processes whose debuggers receive the remote
command entered at the root debugger:
focus_command
: focus expression
| focus all
| focus
The first form of the command sets the current process set to the set
resulting from the evaluation of the given expression. The second form
sets the current process set to the set that includes all processes. The
third form displays the current process set.
As mentioned in the Overview, the root debugger
collects the outputs from the leaf debuggers and presents you with
an aggregated output. In most cases, this aggregation
works fine, but it can be an impediment if you want to know the
exact output from certain leaf debuggers.
To remedy this, the debugger assigns a unique number (called a message_id
)
to each aggregated message and saves the message in the message_id_list
.
You can use the following commands to inspect the message list and expand its entries:
show_aggregated_message_command
: show aggregated message message_id_list
| show aggregated message all
| show aggregated message
message_id_list
: expression {,...}
The first form of the command displays the aggregated messages in the
list whose message IDs match the numbers specified in the message_id_list
.
The second form displays all the aggregated messages in the list. If no message_id
is
specified, the debugger shows the most recently added (newest) message.
expand_aggregated_message_command
: expand aggregated message message_id_list
| expand aggregated message
This command expands the specified messages. If no message_id
is
specified, the debugger expands the most recently added (newest) message.
You can control the length of the message list using the
$aggregatedmsghistory
debugger variable.
If you set this variable to the default (0), the debugger records as many messages as
the system will allow.
This section contains the following tips for debugging parallel applications:
Tip 1. How to Aggregate Outputs
If the debugger outputs are not aggregated as you would expect them to be,
you can change the value of the $parallel_aggregatordelay
debugger variable. The $parallel_aggregatordelay
variable sets the expiration time for each of the aggregators when the aggregators
have not received all the expected messages.
In Aggregated Example 1, the value of $parallel_aggregatordelay
is
set to 100 milliseconds, which does not seem to be long enough for the debugging session's
32 processes. Changing the $parallel_aggregatordelay
variable to 500
milliseconds results in the improved aggregation shown in Aggregated Example 2.
NOTE: Because the default value of the $parallel_aggregatordelay
is 3000 milliseconds, you should not normally have a problem with the aggregation delay. The
purpose of these examples is to show the effect of changing the
$parallel_aggregatordelay
setting.
Aggregated Example 1
% ladebug -parallel /usr/bin/prun -n 32 ~/smg98/test/smg98
Welcome to the Ladebug Debugger Version n
Reading symbolic information ...done
stopped at [void _rms_breakpoint(void):1470 0x3ffbff6053c]
Source file not found or not readable, tried...
./loader.cc
/usr/bin/loader.cc
(Cannot find source file loader.cc)
stopped at [void _rms_breakpoint(void):1470 0x3ffbff6053c]
Source file not found or not readable, tried...
./loader.cc
/usr/bin/loader.cc
(Cannot find source file loader.cc)
Process has exited
(ladebug) [8,13,16:31] Welcome to the Ladebug Debugger Version n
[8,13,16:31] ------------------
[8,13,16:31] object file name: /usr/users/user1/smg98/test/smg98
[8,13,16:31] Reading symbolic information ... [8,13,16:31] done
%1 [8,13,16:31] Attached to process id [14186471;16807748] ....
[8,13,16:31] Thread received signal TRAP
[8,13,16:31] stopped at [ 0x3ff80006790]
[8,13,16:31] Thread received signal TRAP
[8,13,16:31] stopped at [ 0x3ff80020328]
[8,13,16:31] [#1: stop quiet in int main(int, char**) ]
[8,13,16:31] 66 int solver_id;
[8,13,16:31] 67
[8,13,16:31] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[8,13,16:31] 69
[8,13,16:31] 70 HYPRE_StructMatrix A;
(ladebug) [0,2,4] Welcome to the Ladebug Debugger Version n
[0] ------------------
[0] object file name: /usr/users/user1/smg98/test/smg98
[0] Reading symbolic information ... [0] done
[0] Attached to process id 13138577 ....
[0] Thread received signal TRAP
[0] stopped at [ 0x3ff80006790]
[0] Thread received signal TRAP
[1,3,6,15] Welcome to the Ladebug Debugger Version n
[1:2,4,6] ------------------
[1:2,4,6] object file name: /usr/users/user1/smg98/test/smg98
[2,4,6] Reading symbolic information ... [2,4,6] done
%2 [2,4,6] Attached to process id [13138579;13662398] ....
[2,4,6] Thread received signal TRAP
[2,4,6] stopped at [ 0x3ff80006790]
[0] stopped at [ 0x3ff80020328]
[0] [#1: stop quiet in int main(int, char**) ]
[0] 66 int solver_id;
[0] 67
[0] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[0] 69
[0] 70 HYPRE_StructMatrix A;
(ladebug) [9:10] Welcome to the Ladebug Debugger Version n
[3,10,15] ------------------
[3,10,15] object file name: /usr/users/user1/smg98/test/smg98
[1,3,15] Reading symbolic information ... [1,3,15] done
%3 [1,3,15] Attached to process id [13138578;14710852] ....
[1,3,15] Thread received signal TRAP
[1,3,15] stopped at [ 0x3ff80006790]
[1,3,6,15] Thread received signal TRAP
[1,3,6,15] stopped at [ 0x3ff80020328]
[1,3,6,15] [#1: stop quiet in int main(int, char**) ]
[1,3,6,15] 66 int solver_id;
[1,3,6,15] 67
[1,3,6,15] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[1,3,6,15] 69
[1,3,6,15] 70 HYPRE_StructMatrix A;
(ladebug) [9] ------------------
[9] object file name: /usr/users/user1/smg98/test/smg98
[9:10] Reading symbolic information ... [9:10] done
%4 [9:10] Attached to process id [14186472;14186473] ....
[9:10] Thread received signal TRAP
[9:10] stopped at [ 0x3ff80006790]
[9:10] Thread received signal TRAP
[9:10] stopped at [ 0x3ff80020328]
[10] [#1: stop quiet in int main(int, char**) ]
[10] 66 int solver_id;
[10] 67
[10] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[10] 69
[10] 70 HYPRE_StructMatrix A;
(ladebug) [9] [#1: stop quiet in int main(int, char**) ]
[9] 66 int solver_id;
[9] 67
[9] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[9] 69
[9] 70 HYPRE_StructMatrix A;
[5,7,11:12,14] Welcome to the Ladebug Debugger Version n
[5,7] ------------------
[5,7] object file name: /usr/users/user1/smg98/test/smg98
(ladebug) [11:12,14] ------------------
[11:12,14] object file name: /usr/users/user1/smg98/test/smg98
[5,7,11:12,14] Reading symbolic information ... [5,7,11:12,14] done
%5 [5,7,11:12,14] Attached to process id [13662397;14710851] ....
[5,7,11:12,14] Thread received signal TRAP
[5,7,11:12,14] stopped at [ 0x3ff80006790]
[5,7,11:12,14] Thread received signal TRAP
[5,7,11:12,14] stopped at [ 0x3ff80020328]
[5,7,11:12,14] [#1: stop quiet in int main(int, char**) ]
[5,7,11:12,14] 66 int solver_id;
[5,7,11:12,14] 67
[5,7,11:12,14] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[5,7,11:12,14] 69
[5,7,11:12,14] 70 HYPRE_StructMatrix A;
Aggregated Example 2
% ladebug -parallel /usr/bin/prun -n 32 ~/smg98/test/smg98
Welcome to the Ladebug Debugger Version n
Reading symbolic information ...done
stopped at [void _rms_breakpoint(void):1470 0x3ffbff6053c]
Source file not found or not readable, tried...
./loader.cc
/usr/bin/loader.cc
(Cannot find source file loader.cc)
stopped at [void _rms_breakpoint(void):1470 0x3ffbff6053c]
Source file not found or not readable, tried...
./loader.cc
/usr/bin/loader.cc
(Cannot find source file loader.cc)
Process has exited
(ladebug) [0:31] Welcome to the Ladebug Debugger Version n
[0:31] ------------------
[0:31] object file name: /usr/users/user1/smg98/test/smg98
[0:31] Reading symbolic information ... [0:31] done
%1 [0:31] Attached to process id [13138610;16807808] ....
[0:31] Thread received signal TRAP
[0:31] stopped at [ 0x3ff80006790]
[0:31] Thread received signal TRAP
[0:31] stopped at [ 0x3ff80020328]
[0:31] [#1: stop quiet in int main(int, char**) ]
[0:31] 66 int solver_id;
[0:31] 67
[0:31] > 68 int A_num_ghost[6] = { 0, 0, 0, 0, 0, 0};
[0:31] 69
[0:31] 70 HYPRE_StructMatrix A;
(ladebug) next
(ladebug) [0:31] stopped at [int main(int, char**):110 0x120006dd8]
[0:31] 110 MPI_Init(&argc, &argv);
The sample application used in the aggregated examples is located at:
http://www.acl.lanl.gov/30TeraOpRFP/SampleApps/smg98/smg98.html
Tip 2. How to Synchronize Processes
If the processes become unsynchronized in the debugging session (for example,
if you use the focus
command on a subset
of the total set and then use a next
or some other
motion command), the easiest way to get the processes back together is to use
a cont to
a future location where all processes have to go.
The following example shows how the output from processes is not identical because
different processes are at different locations in the
program. Using the cont to
command synchronizes
the processes and aggregates the messages.
(ladebug) next
(ladebug) [4:5,12] stopped at [int feedbackToDebugger(int, int, char*):17 0x120006bf4]
[0:3,6:11] [3] stopped at [int feedbackToDebugger(int, int, char*):15 0x120006bf0]
[4:5,12] 17 int pathSize = 1000;
[0:3,6:11] 15 int i = 0;
(ladebug) l
(ladebug) [0:3,6:11] 16 char path[1000];
[4:5,12] 18 char hostname[1000];
[0:3,6:11] 17 int pathSize = 1000;
[4:5,12] 19 int hostnameSize = 1000;
[0:3,6:11] 18 char hostname[1000];
[4:5,12] 20
[0:3,6:11] 19 int hostnameSize = 1000;
[4:5,12] 21 volatile int debuggerAttached = 0;
[0:3,6:11] 20
[4:5,12] 22
[0:3,6:11] 21 volatile int debuggerAttached = 0;
[4:5,12] 23 gethostname(hostname,hostnameSize);
%3 [0:12] [22;24]
[0:3,6:11] 23 gethostname(hostname,hostnameSize);
[4:5,12] 25 getcwd(path,pathSize);
[0:3,6:11] 24
[4:5,12] 26 strcat(path,"/");
[0:3,6:11] 25 getcwd(path,pathSize);
[4:5,12] 27 strcat(path,name);
[0:3,6:11] 26 strcat(path,"/");
[4:5,12] 28
[0:3,6:11] 27 strcat(path,name);
[4:5,12] 29 // Print myid pid into ladebugAttach.myid
[0:3,6:11] 28
[4:5,12] 30 sprintf(filename,"ladebugAttach.%d",myid);
[0:3,6:11] 29 // Print myid pid into ladebugAttach.myid
[4:5,12] 31 file = fopen(filename,"w");
[0:3,6:11] 30 sprintf(filename,"ladebugAttach.%d",myid);
[4:5,12] 32 if (file == NULL) {
[0:3,6:11] 31 file = fopen(filename,"w");
[4:5,12] 33 fprintf(stderr,"smg98: can't open %s for %s\n",filename, "w");
[0:3,6:11] 32 if (file == NULL) {
[4:5,12] 34 exit(1)
[0:3,6:11] 33 fprintf(stderr,"smg98: can't open %s for %s\n",filename, "w");
[4:5,12] 35 }
[12] 36 fprintf(file," %ld %ld %s %s\n", myid, getpid(), hostname, path);
[12] 37 fclose(file);
[12] 38
[4:5] 36 fprintf(file," %ld %ld %s %s\n", myid, getpid(), hostname, path);
[0:3,6:11] 34 exit(1);
[0:3,6:11] 35 }
[4:5] 37 fclose(file);
[0:3,6:11] 36 fprintf(file," %ld %ld %s %s\n", myid, getpid(), hostname, path);
[4:5] 38
(ladebug) cont to 36
[0:13] stopped at [int feedbackToDebugger(int, int, char*):36 0x120006cb8]
[0:13] 36 fprintf(file," %ld %ld %s %s\n", myid, getpid(), hostname, path);
(ladebug) next
(ladebug) [0:13] stopped at [int feedbackToDebugger(int, int, char*):37 0x120006d0c]
[0:13] 37 fclose(file);
Tip 3. How to Use the -I
Option in the Parallel
Environment
If the application source file is not in the same location as the application
binary file, the debugger will display a message that it cannot find the application
source file. Consider the following example:
$ ladebug -parallel /bin/dmpirun -np 8 -hf ~/test/src/common/Funct/src/cpihostfile \
~/test/src/common/Funct/bin-alpha-osf1/cpi
Welcome to the Ladebug Debugger Version n
Reading symbolic information ...done
stopped at [void MPIR_Breakpoint(void):43 0x120002fc0]
Source file not found or not readable, tried...
./mpirun.c
/bin/mpirun.c
(Cannot find source file mpirun.c)
Process has exited
(ladebug)
[0:7] Welcome to the Ladebug Debugger Version n
[0:7] ------------------
[0:7] object file name: /usr/users/smith/ladebug-sandbox/test/src/common/Funct/bin-alpha-osf1/cpi
[0:7] Reading symbolic information ... [0:7] done
[0:7] Unable to switch to decthreads mode.
[0:7] Source file not found or not readable, tried...
[0:7] ./cpi.c
[0:7] /usr/users/smith/ladebug-sandbox/test/src/common/Funct/bin-alpha-osf1/cpi.c
Specifying the -I
option before the -parallel
option does not fix the problem because the -I
option
applies only to the root debugger. The root debugger only controls the launcher
and will not look outside its location for the application source file. The -I
option is not currently passed along to the leaf debuggers, which would resolve this problem.
The following example shows how to cause the debugger to locate the application source file:
(ladebug) use /usr/proj/debug/ladebug/test/src/common/Funct/src
[0:7] Directory search path for source files:
[0:7] . /usr/users/smith/ladebug-sandbox/test/src/common/Funct/bin-alpha-sf1/usr/proj/debug/ladebug/test/src/common/Funct/src
(ladebug) w
[0:7] 20
[0:7] 21 double f(double);
[0:7] 22
[0:7] 23 int main(int argc, char *argv[])
[0:7] 24 {
[0:7] 25 int done = 0, n, myid, numprocs, i;
[0:7] 26 double PI25DT = 3.141592653589793238462643;
[0:7] 27 double mypi, pi, h, sum, x;
[0:7] 28 double startwtime = 0.0, endwtime;
[0:7] 29 int namelen;
The following is an example of a parallel debugging session. Click on
the links within the example for explanation.
% ladebug -parallel /usr/bin/dmpirun -np 6 /usr/users/parallel/examples/cpi-DmpirunStop
Welcome to the Ladebug Debugger Version n
Reading symbolic information ...done
stopped at [void MPIR_Breakpoint(void):43 0x120002fc0]
Source file not found or not readable, tried...
./mpirun.c
/usr/bin/mpirun.c
(Cannot find source file mpirun.c)
Process has exited
(ladebug) [0:5] Welcome to the Ladebug Debugger Version n
[0:5] ------------------
[0:5] object file name: /usr/users/parallel/examples/cpi-DmpirunStop
[0:5] Reading symbolic information ...done
[0:5] 52 int done = 0, n, myid, numprocs, i;
(ladebug) stop in feedbackToDebugger
[0:5] [#1: stop in int feedbackToDebugger(int, int, char*) ]
(ladebug) focus [0:2]
[0:2]>
[0:2]> cont
[0:2]> [0:2] [1] stopped at [int feedbackToDebugger(int, int, char*):18 0x120001818]
[0:2] 18 int i = 0;
[0:2]> where
[0:2]> %1 [0:2] >0 0x120001818 in feedbackToDebugger(myid=[0;2], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":18
[0:2] #1 0x120001a48 in main(argc=1, argv=0x11fffc018) "cpi-user1.c":63
[0:2] #2 0x120001758 in __start(...) in /usr/users/parallel/examples/cpi-DmpirunStop
[0:2]> focus [3:5]
[3:5]>
[3:5]> cont
[3:5]> [3:5] [1] stopped at [int feedbackToDebugger(int, int, char*):18 0x120001818]
[3:5] 18 int i = 0;
[3:5]> where
[3:5]> %2 [3:5] >0 0x120001818 in feedbackToDebugger(myid=[3;5], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":18
[3:5] #1 0x120001a48 in main(argc=1, argv=0x11fffc018) "cpi-user1.c":63
[3:5] #2 0x120001758 in __start(...) in /usr/users/parallel/examples/cpi-DmpirunStop
[3:5]> focus [*]
[0:5]>
[0:5]> next
[0:5]> [0:5] stopped at [int feedbackToDebugger(int, int, char*):20 0x12000181c]
[0:5] 20 int pathSize = 1000;
[0:5]> where
[0:5]> %4 [0:5] >0 0x12000181c in feedbackToDebugger(myid=[0;5], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":20
[0:5] #1 0x120001a48 in main(argc=1, argv=0x11fffc018) "cpi-user1.c":63
[0:5] #2 0x120001758 in __start(...) in /usr/users/parallel/examples/cpi-DmpirunStop
[0:5]> show aggregated message
%1 [0:2] >0 0x120001818 in feedbackToDebugger(myid=[0;2], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":18
%2 [3:5] >0 0x120001818 in feedbackToDebugger(myid=[3;5], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":18
%3 [0:5] >0 0x12000181c in feedbackToDebugger(myid=[0;5], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":20
[0:5]> expand aggregated message 3
%3 [0:5] >0 0x12000181c in feedbackToDebugger(myid=[0;5], np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":20
[0] >0 0x12000181c in feedbackToDebugger(myid=0, np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":20
[1] >0 0x12000181c in feedbackToDebugger(myid=1, np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":20
[2] >0 0x12000181c in feedbackToDebugger(myid=2, np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-DmpirunStop") "cpi-user1.c":20
[3] >0 0x12000181c in feedbackToDebugger(myid=3, np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-S-BuildDmpirunStop") "cpi-user1.c":20
[4] >0 0x12000181c in feedbackToDebugger(myid=4, np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-S-BuildDmpirunStop") "cpi-user1.c":20
[5] >0 0x12000181c in feedbackToDebugger(myid=5, np=6, name=0x11fffe060="/usr/users/parallel/examples/cpi-S-BuildDmpirunStop") "cpi-user1.c":20
[0:5]> quit
The following are explanatory notes from the previous example:
Component of Example |
Meaning |
-np 6
|
This parallel session creates 6 processes. |
[0:5]
|
This is a message from processes 0 to 5. |
%1
|
This aggregated message contains messages with differing
portions (in this case, the myid parameters are
different from process to process),
and 1 is the message id. |
focus [0:2]
|
This focus command sets the current process set to
include processes 0, 1, and 2. |
[0:2] >
|
This prompt shows the current process set. |
show aggregated message
|
This show aggregated message command
displays all the aggregated messages saved in the message list. |
expand aggregated message 3
|
This expand aggregated message
command expands the aggregated message with message id 3. |
Use the following file, named mpirun_dbg.ladebug
, to start up
the debugger controlling your mpich
processes:
#!/bin/sh
cmdLineArgs=""
p4pgfile=""
p4workdir=""
prognamemain=""
while [ 1 -le $# ] ; do
arg=$1
shift
case $arg in
-cmdlineargs)
cmdLineArgs="$1"
shift
;;
-p4pg)
p4pgfile="$1"
shift
;;
-p4wd)
p4workdir="$1"
shift
;;
-progname)
prognamemain="$1"
shift
;;
esac
done
#
if [ -n "$LADEBUG_HOME" ] ; then
ldbdir=$LADEBUG_HOME
else
ldbdir=""
fi
#
if [ -f $ldbdir/ladebug.cat ] && [ -r $ldbdir/ladebug.cat ] ; then
if [ -n "$NLSPATH" ]; then
nlsmore=$NLSPATH
else
nlsmore=""
fi
NLSPATH=$ldbdir/$nlsmore
fi
#
$ldbdir/ladebug -parallel $prognamemain `eval echo $cmdLineArgs` -p4pg $p4pgfile -p4wd $p4workdir -mpichtv
The debugger has the following predefined variables. Conventionally, a Ladebug debugger variable
name is an identifier with a leading dollar sign ($).
Variable |
Default Setting |
Description |
$aggregatedmsghistory |
0 |
Controls the length of the aggregated message list. If set to the default (0),
the debugger records as many messages as the system will allow.
|
$ascii |
1 |
Prints ASCII or all ISO Latin-1. |
$beep |
1 |
Beeps on illegal command line editing. |
$catchexecs
| 0 |
Stops execution on program exec. |
$catchforkinfork |
0 |
Notifies you as soon as the forked process is created (otherwise you are notified when
the call finishes). |
$catchforks |
0 |
Notifies you on program fork and stops child. |
$childprocess |
0 |
When the debugger detects a fork, it assigns the child process ID to
$childprocess . |
$curevent |
0 |
Displays the current breakpoint number. |
$curfile |
(null) |
Displays the current source file. |
$curfilepath |
(null) |
Displays the current source file access path. |
$curline |
0 |
Displays the current source line. |
$curpc |
0 |
Displays the current point of program execution. |
$curprocess |
0 |
Displays the current process ID. |
$cursrcline |
0 |
Displays the last source line at end of the most recent source listing. |
$cursrcpc |
0 |
Displays the PC address at end of the most recent machine code listing. |
$curthread |
0 |
Displays the current thread ID. |
$dbxoutputformat |
0 |
Displays various data structures in dbx format. |
$dbxuse |
0 |
Replaces current use paths. |
$decints |
0 |
Displays integers in decimal radix. |
$doverbosehelp |
1 |
Displays the help menu front page. |
$editline |
1 |
Enables command line editing. |
$eventecho |
1 |
Echoes events with event numbers. |
$exitonterminationofprocesswithpid |
None |
If set to process ID (pid ), when that process
terminates, the debugger exits. |
$floatshrinking |
1 |
If set to the default (1), the debugger prints
binary floating point numbers
using the shortest
possible decimal number.
If set to 0, the debugger prints the decimal
number that is the closest representation in the
number of decimal digits available of the internal
binary number.
|
$funcsig |
1 |
Displays function signature at breakpoint. |
$giveladebughints |
1 |
Displays hints on Ladebug features. |
$hasmeta |
0 |
Interprets multibyte characters. |
$hexints |
0 |
Displays integers in hex radix. |
$historylines |
20 |
Defines the number of commands to show for history . |
$indent |
1 |
Prints structures with indentation. |
$kdebug_host |
localhost |
Specifies the node name of the gateway
system. The default "localhost" indicates no
gateway system. |
$kdebug_line |
kdebug |
Specifies the label of the /etc/remote
entry on the gateway system for the kdebug communication port. |
$kdebug_dbgtty |
(null) |
Specifies the pathname of the terminal window (output from tty
(1)) on the gateway system used to display kdebug communication
traffic. The default (null) indicates no gateway system.
|
$ladebugpid |
None |
Value of the pid for Ladebug itself. |
$lang |
None |
Defines the programming language of current routine. |
$lasteventmade |
0 |
Displays the number of last (successful) breakpoint definition. |
$lc_ctype |
"C" |
Displays the current locale information. |
$listwindow |
20 |
Displays the number of lines to show for list. |
$main |
"main" |
Displays the name of the first routine in the program. |
$maxstrlen |
128 |
Defines the largest string to print fully. |
$memorymatchall |
0 |
When set to non-zero, displays all memory matches in the specified range.
Otherwise, displays only the first memory match. |
$octints |
0 |
Displays integers in octal radix. |
$overloadmenu |
1 |
Prompts for choice of overloaded C++ name. |
$page |
1 |
Paginates debugger terminal output. |
$pagewindow |
0 |
Defines the number of lines per output page. The default of 0 causes
the debugger to query the terminal for the page size. |
$parallel_aggregatordelay |
3000 milliseconds |
Set in the .ladebugrc
file or or in the file used with the debugger invocation option -i ,
specifies
the length of time that aggregators wait before they aggregate
and send messages down to the next level when not all the expected messages
have been received. |
$parallel_branchingfactor |
8 |
Set in the .ladebugrc
file or in the file used with the debugger invocation option -i ,
specifies the
factor used to build the n-nary tree and determine the
number of aggregators in the tree. |
$parentprocess |
0 |
When the debugger detects a fork, it assigns the parent process ID
to $parentprocess . |
$pimode |
0 |
Echoes input to log file on playback input. |
$prompt |
"(ladebug) " |
Specifies debugger prompt. |
$readtextfile |
0 |
If set to non-zero, reads instructions from the text area of the binary
file rather than from the memory image. |
$regstyle |
1 |
Controls the format of register names during disassembly.
Valid settings are:
- 0 = compiler names, for example, t0, ra, or zero.
- 1 = hardware names, for example, r1, r26, or r31.
- 2 = assembly names, for example, $1, $26, or $31.
|
$repeatmode |
1 |
Repeats previous command when you press the Return key. |
$showlineonstartup |
0 |
Displays the first executable line in main . |
$showwelcomemsg |
1 |
Displays welcome message at startup time. |
$stackargs |
1 |
Shows arguments in the call stack if 1. |
$statusargs |
1 |
Prints breakpoints with parameters if 1. |
$stepg0 |
0 |
Steps over routines with minimal symbols. |
$stoponattach |
0 |
Stops the running process on attach. |
$stopparentonfork |
0 |
Stops parent process execution on fork.
When set to a
nonzero value, this variable
instructs the debugger to stop the parent process after it forks a child process.
The child process continues to run if $catchforks is not
set, otherwise stops.
The default is 0. |
$symbolsearchlimit |
100 |
Specifies the maximum number of symbols that are returned by the
whereis
command for a regular expression search. The default value is 100; a value of 0
indicates no limit. |
$threadlevel |
decthreads |
Specifies POSIX threads (DECthreads) or native threads. |
$usedynamictypes |
1 |
Evaluates using C++ static or dynamic type. |
$verbose |
0 |
Produces even more output. |
The debugger has the following predefined aliases:
(ladebug) alias
F1 print
F2 print 'F2 executes the command "F2 selected-text" - define alias F2'
F3 print 'F3 executes the command "F3 selected-text" - define alias F3'
S next
Si nexti
W list $curline - 10:20
a assign
att attach
b stop at
bp stop in
c cont
d delete
det detach
e file
exit quit
f func
g goto
h history
j status
l list
li ($cursrcpc)/10 i; set $cursrcpc = $cursrcpc + 40
n next
ni nexti
p print
pb printb
pd printd
pi printi
plist show process all
po printo
pr printregs
ps printf "%s",
pt printt
px printx
q quit
r rerun
ri record input
ro record output
s step
si stepi
source playback input
sw switch
switch process
t where
tlist show thread
ts where thread all
tset thread
tstack where thread all
u list $curline - 9:10
w list $curline - 5:10
wi ($curpc - 20)/10 i
wm watch memory
wv watch variable
You can use the following example as an alternative to the listobj
command for cases in which the debugger cannot be run on the original system.
See the Transporting Core Files section for more
information.
/*
cc corefile_listobj.c -lxproc -o corefile_listobj
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
typedef unsigned long vma_t;
/* core file format */
#include <sys/user.h>
#include <sys/core.h>
/* dynamic loader hookup */
#include <loader.h>
typedef int (*ldr_reader_func)(vma_t from,
void * to,
long nbytes,
int is_string);
extern pid_t ldr_core_process();
extern int ldr_set_core_reader(ldr_reader_func reader);
/********************************************************************/
static FILE * corefile;
static struct core_filehdr corehdr;
static int nsections;
static struct core_scnhdr * section_headers;
int
open_corefile(const char * corename)
{
size_t nread;
corefile = fopen(corename, "rb");
if (!corefile) {
perror("Opening corefile");
return -1;
}
nread = fread(&corehdr, sizeof(corehdr), 1, corefile);
if (nread != 1) {
perror("fread() of corefile header");
return -1;
}
if (strncmp(corehdr.magic, "Core", 4) != 0) {
fprintf(stderr, "Corefile header magic is not \"Core\"\n");
return -1;
}
nsections = corehdr.nscns;
section_headers = calloc(nsections, sizeof(section_headers[0]));
if (!section_headers) {
perror("Allocating corefile section headers");
return -1;
}
nread = fread(section_headers, sizeof(section_headers[0]),
nsections, corefile);
if (nread != nsections) {
perror("fread() of corefile section headers");
return -1;
}
return 0;
}
static int
section_type_has_memory(int type)
{
switch (type) {
case SCNTEXT: case SCNDATA: case SCNRGN: case SCNSTACK:
return 1;
case SCNREGS: case SCNOVFL:
default:
return 0;
}
}
static int
read_from_corefile(vma_t from,
void * to,
long nbytes,
int is_string)
{
vma_t getter = from;
char * putter = (char *) to;
long to_go = nbytes;
int secnum;
size_t nxfer;
try_for_more:
while (to_go > 0) {
for (secnum = 0; secnum < nsections; secnum += 1) {
if (section_type_has_memory(section_headers[secnum].scntype)) {
vma_t vaddr = (vma_t) section_headers[secnum].vaddr;
vma_t size = (vma_t) section_headers[secnum].size;
if (vaddr <= getter && getter < vaddr+size) {
vma_t this_time = (size < to_go ? size : to_go);
long file_offset = section_headers[secnum].scnptr+(getter-vaddr);
if (fseek(corefile, file_offset, SEEK_SET) != 0) {
perror("fseek() for corefile read");
return -1;
}
nxfer = fread(putter, 1, this_time, corefile);
if (nxfer != this_time) {
perror("fread() of corefile data ");
return -1;
}
to_go -= this_time;
getter += this_time;
putter += this_time;
goto try_for_more;
}
}
}
fprintf("Couldn't find core address for %#lx\n", getter);
return -1;
}
return 0;
}
int
main(int argc, char* argv[])
{
pid_t process;
if (argc != 2) {
fprintf(stderr, "Usage is %s \n", argv[0]);
return 1;
}
if (open_corefile(argv[1]) < 0)
return -1;
process = ldr_core_process();
ldr_set_core_reader(read_from_corefile);
if (ldr_xattach(process) < 0) {
perror("Attaching to corefile");
return 1;
} else {
ldr_module_t mod_id = LDR_NULL_MODULE;
ldr_module_info_t info;
size_t ret_size;
while (1) {
if (ldr_next_module(process, &mod_id) < 0) {
perror("ldr_next_module");
return 1;
}
if (mod_id == LDR_NULL_MODULE)
break;
if (ldr_inq_module(process, mod_id, &info,
sizeof(info), &ret_size) < 0) {
perror("ldr_inq_module");
return 1;
}
printf("%s\n", info.lmi_name);
}
ldr_xdetach(process);
return 0;
}
}
The debugger provides parameterized aliases and debugger variables of arbitrary
types. Clever use of these can do almost any list traversal.
For example, here is how to navigate an array:
alias elt(e_) "{ p e_ }"
alias pa0(a) "{ set $a = &a[0]; set $i = 0; elt($a[$i]); set $i = $i+1 }"
alias pan "{ elt($a[$i]); set $i = $i+1 }"
pa0
pan
pan
pan
%ladebug a.out
...
(ladebug) alias elt(e_) "{ p e_ }"
(ladebug) alias a0(a) "{ set $a = &a[0]; set $i = 0; elt($a[$i]); set $i = $i+1 }"
(ladebug) alias pan "{ elt($a[$i]); set $i = $i+1 }"
...
(ladebug) pa0(a)
struct S {
next = 0x140000178;
}
(ladebug) pan
struct S {
next = 0x140000180;
}
(ladebug)
struct S {
next = 0x140000188;
}
(ladebug)
struct S {
next = 0x140000190;
}
Compaq, the Compaq logo, Alpha, AlphaServer, AlphaStation, DECconnect, and Tru64
are trademarks of Compaq Information Technologies Group, L.P., in the U.S. and/or
other countries.
Motif, UNIX, and The Open Group are trademarks of The Open Group in the U.S. and/or
other countries.
All other product
names mentioned herein may be trademarks of
their respective companies.
Confidential computer software. Valid license from Compaq required for
possession, use, or copying. Consistent with FAR 12.211 and 12.212,
Commercial Computer Software, Computer Software Documentation, and
Technical Data for Commercial Items are licensed to the U.S. Government
under vendor's standard commercial license.
Compaq shall not be liable for technical or editorial errors or
omissions contained herein. The information in this publication is
provided "as is" without warranty of any
kind and is subject to change without notice.
The warranties for Compaq products are set forth in
the express limited warranty statements accompanying such products.
Nothing herein should be
construed as constituting an additional warranty.