This chapter describes how to examine and change data in your program while running it under dbx. Topics covered include:
Many dbx commands accept one or more expressions as arguments. Expressions can consist of constants, dbx variables, program variables, and operators. This section discusses operators and constants. "Creating and Removing dbx Variables" describes dbx variables, and "Displaying and Changing Program Variables" describes program variables.
In general, dbx recognizes most expression operators from C, Fortran 77, and Pascal. dbx also provides some of its own operators. Operators follow the C language precedence. You can also use parentheses to explicitly determine the order of evaluation.
Table 5-1 lists the operators provided by dbx.
Table 5-1. dbx Language Independent Operators
Operator | Description |
---|---|
not | Unary operator returning false if the operand is true |
or | Binary logical operator returning true if either operand is nonzero |
xor | Binary operator returning the exclusive-OR of its operands |
/ | Binary division operator ("//" also works for division) |
div | Binary operator that coerces its operands to integers before dividing |
mod | Binary operator returning op1 modulo op2. This is equivalent to the C "%" operator |
#exp | Unary operator returning the address of source line specified by exp |
"file"#exp | Unary operator returning the address of source line specified by exp in the file specified by file |
proc #exp | Unary operator returning the address of source line specified by exp in the file containing the procedure proc |
The # operator takes the line number specified by the expression that follows it and returns the address of that source line. If you precede the # operator with a filename enclosed in quotation marks, the # operator returns the address of the line number in the file you specify. If you precede the # operator with the name of a procedure, dbx identifies the source file that contains the procedure and returns the address of the line number in that file.
For example, to print the address of line 27 in the current source file, enter:
(dbx) print #27 |
To print the address of line 27 in the source file foo.c (assuming that foo.c contains source that was used to compile the current object file), enter:
(dbx) print "foo.c" #27 |
To print the address of line 27 in the source file containing the procedure bar (assuming that bar is a function in the current object file), enter:
(dbx) print bar #27 |
Note: A pound sign (#) introduces a comment in a dbx script file. When dbx sees a pound sign in a script file, it interprets all characters between the pound sign and the end of the current line as a comment. See "Executing dbx Scripts" for more information on dbx script files. To include the # operator in a dbx script, use two pound signs (for example, ##27). |
Table 5-2 lists the C language operators recognized by dbx.
Table 5-2. C Language Operators Recognized by dbx
Type | Operators |
---|---|
Unary | ! & + - * sizeof() |
Binary | % << >> == <= >= != < > & && | || + - * / [ ] -> . |
Note: C does not allow you to use the sizeof operator on bit fields. However, dbx allows you to enter expressions using the sizeof operator on bit fields; in these cases, dbx returns the number of bytes in the data type of bit fields (such as int or unsigned int). The C language "^" exclusive-OR operator is not supported. Use the dbx "xor" operator instead. |
Table 5-3 lists the Pascal operatorsrecognized by dbx.
Table 5-3. Pascal Operators Recognized by dbx
Type | Operators |
---|---|
Unary | not ^ + - |
Binary | mod = <= >= <> < > and or + - * / div [ ] |
Table 5-4 lists the Fortran 77 and Fortran 90 language operators recognized by dbx. Note that dbx does not recognize Fortran logical operators (such as .or. and .TRUE.).
Table 5-4. Fortran 77 and Fortran 90 Operators Recognized by dbx
Type | Operators |
---|---|
Unary | + - |
Binary | + - * / % |
Note: Fortran array subscripts may be in either square brackets, [ ], or the standard parenthesis, ( ), and the Fortran 90 member selection operator (%) is allowed. |
You can use both numeric and string constants under dbx.
In numeric expressions, you can use any valid integer or floating point constants. By default, dbx assumes that numeric constants are in decimal. You can set the default input base to octal by setting the dbx variable $octin to a nonzero value. You can set the default input base to hexadecimal by setting the dbx variable $hexin to a nonzero value. If you set both $octin and $hexin to nonzero values, $hexin takes precedence.
You can override the default input type by prefixing "0x" to indicate a hexadecimal constant, or "0t" to indicate a decimal constant. For example, "0t23" is decimal 23 (which equals hexadecimal 0x17), and "0x2A" is hexadecimal 2A (which equals decimal 42).
By default, dbx prints the value of numeric expressions in decimal. You can set the default output base to octal by setting the dbx variable $octints to a nonzero value. You can set the default output base to hexadecimal by setting the dbx variable $hexints to a nonzero value. If you set both $octints and $hexints to nonzero values, $hexints takes precedence.
Most dbx expressions cannot include string constants. The print and printf commands are two of the dbx commands that accept string constants as arguments. You can also use the set command to assign a string value to a dbx variable.
Otherwise, string constants are useful only as arguments to functions that you call interactively. See "Using Interactive Function Calls" for information on interactive function calls.
You can use either the double-quotation mark (") or the single-forward quotation mark (') to quote strings in dbx. In general, dbx recognizes the following escape sequences in quoted strings (following the standard C language usage):
\\ \n \r \f \b \t \' \" \a |
Enclosing a character string in back quotation marks (`) indicates that the whole string is the name of a program element, not a character-string constant. This is useful, for example, when referring to C++ templates, which include in their names the greater-than (>) and less-than (<) characters. Without back quotation marks, dbx would attempt to interpret the characters as operators. For further discussion, see the sections "Qualifying Names of Program Elements" in this chapter and "Referring to C++ Functions" in Chapter 6.
dbx provides the following commands for printing values of expressions:
print [exp1 [, exp2, ...] ] |
| |
printd [exp1 [, exp2, ... ] ] |
| |
printo [exp1 [, exp2, ... ] ] |
| |
printx [exp1 [, exp2, ... ] ] |
|
For displaying information about variables, the duel command is a flexible alternative to the print command; see "Using the High-Level Debugging Language duel".
The variable types are listed in Table 5-5.
Type | Variable Name | Value |
---|---|---|
signed char | sc | 0xff |
unsigned char | usc | 0xff |
signed short | ssh | 0xffff |
unsigned short | ush | 0xffff |
Examples include:
(dbx) pd sc -1 (dbx) pd ssh -1 (dbx) px sc 0xff (dbx) px ssh 0xffff (dbx) pd usc 255 (dbx) pd ush 65535 |
dbx always prints the bits in the appropriate type. pd is an exception; it expands signed types with sign extension so the decimal value looks correct.
Another example:
(dbx) print sc, usc '\377' '\377' |
If $hexchars is set, this command displays `0xff' `0xff'. (This is a change from releases previous to IRIX 5.2. Previously, the px, po cases on signed char expanded to 32 bits, so px sc printed 0xffffffff.)
If the printed data type is pointer, dbx uses the format specified in the $addrfmt or $addrfmt64 predefined dbx variable ($addrfmt64 is used on only 64-bit processes).
You can use data types for type conversion (casting) by including the name of the data type in parentheses before the expression you want to cast. For example, to convert a character into an integer, use (int) to cast the value:
(dbx) print (int) 'b' 98 |
To convert an integer into a character, use (char) to cast the value:
(dbx) print (char) 67 'C' |
This is standard C language type casting.
You can use the same name for different program elements, such as variables, functions, statement labels, several times in a program. This is convenient and, during program execution, the potential ambiguity presents no problem. For example, you can use a temporary counter named "i" in many different functions. The scope of each variable is local; space is allocated for it when the function is called and freed when the function returns. However, in dbx you sometimes need to distinguish occurrences of identical names.
dbx allows unambiguous reference to all program elements by using source file and routine names as qualifying information that makes otherwise indistinguishable names unique. To find the fully qualified name of the active version of a name, use the which command. To find the fully qualified names of all versions of a name, use the whereis command. Note that if a variable, such as i, is used many times, whereis can generate many lines of output.
The fully qualified name of a program element allows you not only to refer within a procedure to variables of the same name with different scopes, but to refer unambiguously to program elements outside your current frame or activation stack.
dbx qualifies names with the file (also called module), the procedure, a block, or a structure. You can manually specify the full scope of a variable by separating scopes with periods. For example:
mrx.main.i |
In this expression, i is the variable name, main is a procedure in which it appears, and mrx is the source file (omitting the file extension) in which the procedure is defined.
For languages without `modules,' such as C, C++, and Fortran, the base name of the source file, that is the file name up to the first dot in the name, is taken as a module name. For example, if b is a Fortran subroutine in t.f, then t.b names the routine.
To illustrate how names are qualified, consider a C program called test that contains a function compare. In this example, the variable i is declared in both the main procedure and the compare function:
int compare ( int ); main( argc, argv ) int argc; char **argv; { int i; ... } int compare ( arg1, arg2 ) { int i; ... } |
To trace the value of the i that appears in the function compare, enter:
(dbx) trace test.compare.i |
To print the value of the i that appears in the procedure main, enter:
(dbx) print test.main.i |
It is possible to have variable scopes in C and C++ that are in unnamed program blocks. dbx provides names for these scopes, starting with __$$blk1 and followed by __$$blk2, __$$blk3, etc, which it places in the fully qualified name of the variable as it would an explicit scope name. The whereis and which commands show the names assigned.
dbx provides a special name __aout for your base executable. So for example, you can use __aout.main to refer to a global C function main in your executable. You can, of course, also refer to the function using the name of your executable; if your executable is named myaout, myaout.main also refers to the global C function main.
If you wish to refer to a variable that occurs in a DSO, dbx uses a naming convention similar to that for variables in your executable. If, for example, strcpy is a function from the file stuff.c in the library libc.so.1, then both libc.stuff.strcpy and libc.strcpy refer to the function strcpy.
In C, struct, union, and enum tags can conflict with other names. From the context, dbx usually interprets correctly a reference to one of these tags. However, if dbx does not, prefix the tag with the marker __$T_ to prevent confusion with variables or functions. For example; use __$T_foo if you wish to refer to:
struct foo { /* ... */ } |
In ANSI C, statement label names also can conflict with other names. The ambiguity is removed by applying a prefix of __$L_ to the label name. Thus, for example:
int myfoo { int x; x: goto x; ++x} |
If you enter:
(dbx) print &x |
The output is the address of the variable x. If you enter:
(dbx) print &__$L_x |
The output is the address of label x. The –32 compiler provides no debugging information on C labels.
To refer to Fortran statement labels you must either use the __$L_ prefix or use back quotation marks to force dbx to recognize a numerical label as a name. For example, if you have:
do 10 i = 1,10 10 continue |
Both of the following commands lists the address of the label:
(dbx) print &`10` (dbx) print &__$L_10 |
You may have multiple source files with the same name, for example myfile.c, that are in different directories. The `module' name myfile may refer to either source file. dbx resolves this ambiguity by prefixing the fully qualified file names with a unique, numeric label. The which and whereis commands show the label used. For example, suppose the main executable has two myfile.c source files, then __aout.myfile refers to either source file, __aout._$1_myfile refers to one of them, and __aout._$2_myfile refers to the other.
A leading dot (a period at the beginning of the identifier) tells dbx that the first qualifier is not a module (file).
The leading dot is useful when a file and a procedure have the same name. For instance, suppose mrx.c contains a function called mrx. Further, suppose that mrx.c contains a global variable called mi and a local variable, also called mi. To refer to the global variable, use the qualified form .mrx.mi, and to refer to the local variable, use the qualified form mrx.mrx.mi.
You can use the value of program variables in dbx expressions. You can also change the value of program variables while running your program under dbx control.
You can access the value of a variable only while it is in scope. The variable is in scope only if the block or procedure with which it is associated is active.
After you start your program, whenever your program executes a block or procedure that contains variables, your program allocates space for those variables and they "come into scope." You may access the values of those variables as long as the block or procedure is active. Once the block or procedure ends, the space for those variables is deallocated and you may no longer access their values.
You can display the value of a program variable using the print, printd, printf, printo, and printx commands and the pd, po, and px aliases described in "Printing Expressions". For example, to print the value of the program variable total, enter:
(dbx) print total 235 |
The print command also displays arrays, structures, and other complex data structures. For example, if message is a character array (a string), dbx prints the string:
(dbx) print message "Press <Return> to continue." |
As a more complex example, consider a simple linked list stored as an array of elements, each element consisting of a pointer to the next element and an integer value. If the array is named list, print the entire array by entering:
(dbx) print array |
dbx prints the value of each element in the array:
{ [0] struct list { next = (nil) value = 1034 } [1] struct list { next = 0x10012258 value = 1031 } [2] struct list { next = 0x10012270 value = 1028 } [3] struct list { next = 0x10012288 value = 1025 } [4] struct list { next = 0x100122a0 value = 1022 } [5] struct list { next = 0x100122b8 value = 1019 } ... } |
To print an individual element, enter a command such as:
(dbx) print array[5] struct list { next = 0x100122b8 value = 1019 } |
If your array consists of simple elements such as integers or floating point values, you can directly examine the contents of memory using the / (examine forward) command described in "Examining Memory and Disassembling Code".
Suppose a single-precision floating point array is named float_vals. To see the six consecutive elements beginning with the fifth element, enter:
(dbx) &float_vals[4] / 6f 10012018: 0.25000000000000000 0.20000000298023224 0.16666699945926666 0.14280000329017639 10012028: 0.12500000000000000 0.11111100018024445 |
You can also print parts of arrays and complex structures with duel, a high-level debugging language. For more information, see "Using the High-Level Debugging Language duel."
The assign command changes the value of existing program variables. You can also use the assign command to change the value of machine registers, as described in "Changing Register Values".
The syntax of the assign command is:
For example:
(dbx) assign x = 27 27 (dbx) assign y = 37.5 37.5 |
If you receive an incompatible types error when you try to assign a value to a pointer, use casts to make the assignment work. For example if next is a pointer to a structure of type "element," you can assign next a null pointer by entering:
(dbx) assign *(int *) (&next) = 0 0 (dbx) assign next = 0 (nil) (dbx) assign next = (struct list*) 0; (nil) |
In this example, nil denotes that the value of the pointer is 0; nil is similar to NULL in the C language.
When naming variables in your program, avoid using any dbx keywords. If you have a variable with the same name as a dbx keyword and you attempt to use that variable in a dbx command, dbx reports a syntax error.
If you do have a program variable with the same name as a dbx command, you can force dbx to treat it as a variable by enclosing the variable in parentheses. For example, if you try to print the value of a variable named in by entering the following command, dbx displays an error.
(dbx) print in |
print in ^ syntax error Suggestion: in is a dbx keyword; a revised command is in history. Type !16 or !! to execute: print (in) |
The correct way to display the value of input is to enter:
(dbx) print (in) 34 |
dbx keywords include:
all not and or at pgrp div pid if sizeof in to mod xor |
Whether or not dbx is case sensitive when it evaluates program variable names depends on the value of the dbx variable $casesense.
If $casesense is 2 (the default), then the language in which the variable was defined is taken into account (for example, C and C++ are case sensitive while Pascal and Fortran are not). If $casesense is 1, case is always checked. If $casesense is 0, case is always ignored. Note that file (module) names are always case sensitive since they represent UNIX file names.
You can control the values of environment variables used by a program without affecting the shell. The dbx commands printenv, setenv, and unsetenv control the debugging environment much like their csh counterparts control the shell environment. However, they only affect your program while it is being debugged. dbx passes the values shown by the printenv command to the shell as the environment to use while your program is run by the run or rerun commands.
The following commands control your program's environment variables:
If you attempt to change the environment variables PAGER or EDITOR, the effect on dbx is undefined; the new values may, or may not, affect how dbx runs.
The duel language is a high-level debugging language that you can invoke from dbx. duel is an acronym for Debugging U (might) Even Like.
The duel language provides the following commands for printing parts of arrays and complex structures:
duel | Invokes the duel debugging language. | |
duel alias | Shows all current duel aliases. | |
duel clear | Deletes all duel aliases. |
To invoke duel from within dbx, type:
(dbx) duel |
For example, to print the array elements x[1] to x[10] that are greater than 5, enter:
(dbx) duel x[1..10] >? 5 x[3] = 14 x[8] = 6 |
The output includes the values 14 and 6, as well as their symbolic representation x[3] and x[8].
The duel language is implemented by adding the duel command to dbx. All dbx commands work as before. The duel command, however, is interpreted by duel, and its concepts are not understood by other dbx commands.
Note: This version of duel does not understand or allow interactive function calls. |
duel is based on expressions that return multiple values. The x..y operator returns the integers from x to y; the x,y operator returns x and then y. For example:
(dbx) duel (1,9,12..15,22) |
This command prints 1, 9, 12, 13, 14, 15, and 22. You can use such expressions wherever a single value is used. For example:
(dbx) duel x[1,9,12..15,22] |
This command prints elements 1, 9, 12, 13, 14, 15, and 22 of the array x. duel incorporates C operators, and casts C statements as expressions.
The semicolon (;) prevents duel output. duel aliases are defined with x:=y and provide an alternative to variable declaration. You can also return x[i] instead of using printf:
(dbx) duel if(x[i:=0..99]<0) x[i] x[i] = -4 |
The symbolic output x[i] can be fixed by surrounding i with {}. For example:
(dbx) duel if(x[i:=0..99]<0) x[{i}] x[7] = -4 |
The {} are like (), but force the symbolic evaluation to use i's value, instead of i. You can usually avoid this altogether with direct duel operators:
(dbx) duel x[..100] <? 0 x[7] = -4 |
The ..n operator is a shorthand for 0..n-1. For example, ..100 is the same as 0..99. The x<?y, x==?y, x>=?y, and so forth, operators compare their left side operand to their right side operand as in C, but return the left side value if the comparison result is true. Otherwise, they look for the next values to compare, without returning anything.
duel's x.y and x->y allow an expression y, evaluated under x's scope:
(dbx) duel emp[..100].(if(code>400) (code,name)) emp[46].code = 682 emp[46].name = "Ela" |
The if() expression is evaluated under the scope of each element of emp[], an array of structures. In C language terms, we have to write:
for(i = 0; i < 100; i++ ) { if(emp[1].code > 400) { printf("%d %s\n",emp[i].cond,emp[i].name); |
A useful alternative to loops is the x=>y operator. It returns y for each value of x, setting the underbar (_) to reference x's value. For example:
(dbx) ..100 => if(emp[_].code>400) emp[_].code,emp[_].name |
Using _ instead of i also avoids the need for {i}. Finally, the x-->y operator expands lists and other data structures. If head points to a linked list threaded through the next field, then:
(dbx) duel head-->next->data head->data = 12 head->next->data = 14 head-->next[[2]]->data = 20 head-->next[[3]]->data = 26 |
This produces the data field for each node in the list. x-->y returns
x, x->y, x->y->y, x->y->y->y, ... until a NULL is found. The symbolic output x-->y[[n]] indicates that ->y was applied n times. x[[y]] is also the selection operator:
(dbx) duel head-->next[[50..60]]->data |
This example returns the 50th through the 60th elements in the list. The #/x operator counts the number of values. For example:
(dbx) duel #/( head-->next->data >? 50 ) |
This example counts the number of data elements over 50 on the list. Several other operators, including x@y, x#y, and active call stack access are described in the "duel Operators."
Most duel operators have the same precedence as their C counterparts. Table 5-6 lists duel operators in decreasing precedence.
Table 5-6. duel Operator Summary
Associativity | Operators | Details |
---|---|---|
left | {} () [] -> . f() --> | x-->y expands x->y x->y->y ... |
| x[[y]] x#y x@y | generate x; select, index or stop-at y |
right | #/ - * & ! ~ ++ -- (cast) | #/x number of x values |
| frame(n) sizeof(x) | reference to call stack activation level n |
| = | simple assignment |
left | x/y x*y x%y | multiply, divide, reminder |
left | x-y x+y | add, subtract |
left | x<<y x>>y | shift left/right |
none | x..y ..y x.. | ..y 0..y-1. x..y return x, x+1...y |
left | < > <= >= <? >? <=? >=? | x>?y return x if x>y |
left | == != ==? !=? | x==?y return x if x=y |
left | x&y | bit-and |
left | x^y | bit-xor |
left | x|y | bit-or |
left | x&&y &&/x | &&/x are all x values nonzero? |
left | x||y ||/x | ||/x is any x value nonzero? |
right | x? y:z | for each x, if(x) y else z |
right | x:=y | x:=y set x as a duel alias to y |
left | x,y | return x, then y |
right | x=>y | for each x, evaluate y with x value `_' |
right | if() else while() for() | C statements cast as operators |
left | x;y | evaluate and ignore x, return y |
right | ,, | Fortran multi-dimensional array separator: x[7,,5]. Note the square brackets; x(7,,5) will not work in duel. |
Table 5-7 lists and briefly explains duel examples.
Example | Explanation |
---|---|
duel (0xff-0x12)*3 | compute simple expression |
duel (1..10)*(1..10) | display multiplication table |
duel x[10..20,22,24,40..60] | display x[i] for the selected indexes |
duel x[9..0] | display x[i] backwards |
duel x[..100] >? 5 | display x[i] that are greater than 5 |
duel x[..100] >? 5 <? 10 | display x[i] if 5<x[i]<10 |
duel x[..100] ==? (6..9) | same as above |
duel x[0..99]=>if(_>5 && _<10) _ | same as above |
duel y[x[..100] !=? 0] | display y[x[i]] for each nonzero x[i] |
duel emp[..50].code | display emp[i].code for i=0 to 49 |
duel emp[..50].(code,name) | display emp[i].code & emp[i].name |
duel val[..50].(is_dbl? x:y) | display val[i].x or val[i].y depending on val[i].is_dbl. |
duel val[..50].if(is_dbl) x else y | same as above |
duel (hash[..1024]!=?0)->scope | hash[i].scope for non-null hash[i] |
duel x[i:=..100] >? x[i+1] | check if x[i] is not sorted |
duel x[i:=..100] ==? x[j:=..100]=> if(i<j) x[{i,j}] | check if x has nonunique elements |
duel if(x[i:=..99] == x[j:=i+1..99]) x[{i,j}] | same as above |
duel (x[..100] >? 0)[[0]] | the 1st (0th element) positive x[i] |
duel (x[..100] >? 0)[[2]] | return the 3rd positive x[i] |
duel (x[..100] >? 0)[[..5]] | return the first 5 positive x[i] |
duel (x[0..] >? 6)[[0]] | return the first x[i]>6, no limit on i |
duel argv[0..]@0 | argv[0] argv[1] .. until first null |
duel x[0..]@20 >? 9 | x[0..n]>9 where n is first x[n]==20 |
duel emp[0..]@(code==0) | emp[0]..emp[n-1] where emp[n].code==0 |
duel head-->next->val | val of each element in a linked list |
duel head-->next[[20]] | the 21st element of a linked list |
duel *head-->next[[20]] | display above as a struct |
duel #/head-->next | count elements on a linked list |
duel x-->y[[#/x-->y - 1]] | last element of a linked list |
duel x-->y[[#/x-->y - 10..1]] | last 10 elements of a linked list |
duel head-->next-> if(next) val >? next->val | check if the list is sorted by val |
duel head-->(next!=?head) | expand cyclic linked list (tail->head) |
duel head-->(next!=?_) | handle termination with p->next==p |
duel root-->(left,right)->key | expand binary tree, show keys |
duel root-->(left,right)->( (left!=?0)->key>=?key, (right !=?0 )->key<=?key) | check bin tree sorted by key |
duel (T mytype) x | convert x to user defined type mytype |
duel (struct s*) x | convert x to struct s pointer |
duel if(x) y; else z *ERR* | `;' must be followed by an expression |
duel {x} y *ERR* | `}' requires `;' if followed by exp |
fortarray[2..5,, 6,7] | print 2-dimensional Fortran array elements |
The duel semantics are modeled after the Icon programming language. The input consists of expressions that return sequences of values. C statements are cast as expressions, too. Expressions are parsed into abstract syntax trees, which are traversed during evaluation. The evaluation of most nodes (operators) recursively evaluates the next value for each operand, and then applies the operator to produce the next result. Only one value is produced each time, and duel's eval function keeps a state for each node (backtracking, co-routines, consumer-producer or threads are good metaphors for the evaluation mechanism).
For example, in (5,3)+6..8, the evaluation of `+' first retrieves the operands 5 and 6, to compute and return 5+6. Then 7, the next right operand is retrieved and 5+7 is returned, followed by 5+8. Since no other right operand value exists, the next left operand, 3 is fetched. The right operand's computation is restarted returning 6, and 3+6 is returned. The final return values are 3+7 and 3+8.
The computation for operators like x>?y is similar, but when x<=y, the next values are fetched instead of returning a value, forming the basis for an implicit search. Operators like .. return a sequence of values for each pair of operands.
The duel values follow the C semantics. A value is either an lvalue (can be used as the left-hand side of assignment), or an rvalue. Therefor, objects like arrays can not be directly manipulated. However, operators like x..y can accomplish such tasks.
The duel types also follow the C semantics, with some important differences. C types are checked statically; duel types are checked when operators are applied. For example, (1,1.0)/2 returns 0 (int) and 0.5 (double); (x,y).z returns x.z and y.z even if x and y are of different types, as long as they both have a field z.
Values and types of symbols are looked up at run-time (using the dbx lookup rules).
To avoid this ambiguity, the keyword T must precede a user-defined type. For example, if value is a typedef, C's (value (*)()) x is written in duel as
(T value (*)()) x. Types that begin with a reserved keyword don't need T. For example, (struct value*) x and (long *[5]) y are accepted. As special cases, (type)x and (type*)x are accepted but discouraged (it causes (printf)("hi"), which is valid in C, to fail). A side effect is that sizeof x must be written as sizeof(x).
The duel operators are described below.
x=y x+y x-y x*y x/y x%y x^y x|y x&y x<<y x>>y
The following paragraphs describe the differences between duel, C, and Fortran languages.
Both {} and ; are operators, not statements or expression separators. For example, if(x) y; else {z;} u is illegal; use if(x) y else {z} ; u. Ambiguities require preceding user-defined types (typedef) with the keyword T. For example, if value is a user type, C's sizeof(value*) is written sizeof(T value*), except for the casts (t)x and (t*)x; sizeof(x) requires parenthesis for variable x.
Because the comma (,) is used to separate a sequence of values, the usual dbx syntax for multi-dimensional array references of myarr[3,4] does not mean the same thing to duel as it does to dbx.
In duel, refer to the dimensions of a multi-dimensional Fortran array using ,, as the dimension separator. In other words, if myarr is a two-dimensional array, myarr[3,,4] refers to the Fortran array element myarr(3,4).
The base dbx syntax for this element remains unchanged. For example, to show that element of myarr, use one of the following:
(dbx) print myarr[3,4] (dbx) duel myarr[3,,4] |
The which command allows you to determine the scope of a variable. This command is useful for programs that have multiple variables with the same name occurring in different scopes.
The which command prints the fully qualified name of the active version of a specified variable. For example, to determine the scope of the variable i, enter:
(dbx) which i .foo.foo2.i |
In the example above, the variable i that is currently active is local to the procedure foo2 that appears in the module foo (corresponding to the file foo.c in a C language program).
The which command also determines the fully qualified name of other program elements, such as procedures or type descriptors, that are submitted as arguments for the command.
The whereis command prints the fully qualified names of all versions of the name of any program element. dbx searches (a possibly limited part of) your program for all occurrences of the name and returns the fully qualified names. The range of the search is determined by the dbx variable $whereisdsolimit. By default, $whereisdsolimit is 1 and only the main executable is checked by whereis. To search all objects, set $whereisdsolimit to 0. To check just the first n objects, set $whereisdsolimit to n.
The whatis command displays the type declaration for a specified variable or procedure in your program.
For example, to display the type declaration for the variable i, enter:
(dbx) whatis i int i; |
The following example illustrates the output of whatis for an array of structures:
(dbx) whatis array struct list { struct list* next; int value; } array[12]; |
When you provide a procedure name to whatis, dbx reports the type of the value returned by the procedure and the types of all arguments to the procedure:
(dbx) whatis foo int foo(i) int i; (dbx) whatis main int main(argc, argv) int argc; char** argv; |
Each time your program executes a procedure, the information about where in the program the call was made from is saved on a stack. The stack also contains arguments to the procedure and all of the procedure's local variables. Each procedure on the stack defines an activation level or frame. Activation levels can also consist of blocks that define local variables within procedures.
The most recently called procedure or block is numbered 0. The next active procedure (the one that called the current procedure) is numbered 1. The last activation level is always the main program block.
The stack determines the scope of many dbx commands and expressions. For example, unless you qualify a variable, as described in "Qualifying Names of Program Elements"dbx assumes that variables you reference are local to the current activation level. If a variable does not appear in the current activation level, dbx successively examines previous activation levels in the stack until it finds the referenced variable. The maximum number of activation levels examined is determined by the dbx variable $stacktracelimit, which has a default value of 100.
The where command prints stack traces. Stack traces show the current activation levels (procedures) of a program. For example, consider the following stack trace for a program called test:
(dbx) where > 0 foo2(i = 5) ["/usr/var/tmp/dbx_examples/test.c":44, 0x1000109c] 1 foo(i = 4) ["/usr/var/tmp/dbx_examples/test.c":38, 0x1000105c] 2 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test.c":55, 0x10001104] 3 __start() ["/shamu/lib/libc/libc_64/crt1text.s":137, 0x10000ee4] |
This program has four activation levels. The most recent, a call of the procedure foo2, is numbered 0. The currently selected activation level is 0, indicated by the ">" character.
The stack trace also reports that foo2 was passed one argument: the value 5 was assigned to the local variable i. The trace indicates that the program was stopped at line 44 of the file test.c, which translates to machine address 0x1000109c.
The stack trace reports similar information for the next two activation levels in this example. You can see that the function foo called foo2 from line 38 in test.c. In turn, foo was called by main at line 55 of the file test.c. Finally, the run-time start-up level was called at line 137 from the file ctrltext.s.
If a program is highly recursive, stack traces can get quite long. The dbx variable $stacktracelimit controls the maximum number of activation levels that appear in a stack trace. In the example above, setting $stacktracelimit = 2 before issuing the where command reduces the set of reported frames to just levels 0 and 1.
If you compile with –g0 or with no –g option, limited symbols are reported. In cases such as this, where detailed symbolic information is not available, the four hexadecimal values returned represent dbx's guess that the function has four integer arguments. The following example illustrates such a case:
(dbx) where > 0 fooexample(0x300000000, 0x4000000ff, 0x5000000ff, 0x0) ["/usr/var/tmp/dbx_examples/test3.c":10, 0x10000cf8] 1 main(0x3, 0x4, 0x5, 0x0) ["/usr/var/tmp/dbx_examples/test3.c":5, 0x10000cbc] 2 __start() ["/shamu/lib/libc/libc_64/csu/crt1text.s":137, 0x10000c64] (dbx) quit Process 22582 terminated int fooexample(int,int,int); int main() { fooexample(3,4,5); return 0; } int fooexample(int i, int j, int k) { int x = i + j + 3*k; return x; } |
The examples below show register values from code compiled without a –g option. MIPS1 or MIPS2 code using the 32-bit ABI (for example, on an Indy):
(dbx) where > 0 subr1(0x3, 0x7fffaf14, 0x7fffaf1c, 0x0) ["t.c":3, 0x4009ec] 1 test(0x3, 0x7fffaf14, 0x7fffaf1c, 0x0) ["t.c":8, 0x400a10] 2 main(0x1, 0x7fffaf14, 0x7fffaf1c, 0x0) ["t.c":13, 0x400a48] 3 __start() ["crt1text.s":133, 0x40099c] |
There are four hexadecimal values displayed in most lines of the code above since the 32-bit MIPS ABI has four integer argument passing registers. No user-useful registers are passed to __start().
MIPS3 or MIPS4 code using the 64-bit ABI (for example, on a Power Challenge):
(dbx) where > 0 subr1(0x3, 0xffffffaed8, 0xffffffaee8, 0x0, 0x2f, 0x10, 0x0, 0xfbd82a0) ["/usr/people/doc/debug/t.c":3, 0x10000c9c] 1 test(0x3, 0xffffffaed8, 0xffffffaee8, 0x0, 0x2f, 0x10, 0x0, 0xfbd82a0) ["/usr/people/doc/debug/t.c":9, 0x10000ce8] 2 main(0x1000000ff, 0xffffffaed8, 0xffffffaee8, 0x0, 0x2f, 0x10, 0x0, 0xfbd82a0) ["/usr/people/doc/debug/t.c":14, 0x10000d2c] 3 __start() ["/shamu/redwood2/work/irix/lib/libc/libc_64/csu/crt1text.s":137, 0x10000c70] |
There are eight hexadecimal values displayed in most lines of the code above since the 64-bit MIPS ABI has eight integer argument passing registers. No user-useful registers are passed to __start().
The values listed as arguments are the integer argument-passing register values. Typically, only the 0 entry of the stack has those argument values correct. Correctness is not guaranteed because the code generator can overwrite the values, using the registers as temporary variables.
The debugger reports the integer argument-passing registers because this information may be of some value.
For example, for the code samples above, the following code calls subr1():
int test(void) { subr1(3); } |
This code displays 0x3 as the argument register value. The other registers listed for subr1 contain arbitrary data.
The up and down commands move up and down the activation levels in the stack. These commands are useful when examining a call from one level to another. You can also move up and down the activation stack with the func command described in "Moving to a Specified Procedure".
The up and down commands have the following syntax:
up [num] | Moves up the specified number of activation levels in the stack. The default is one level. | |
down [num] | Moves down the specified number of activation levels in the stack. The default is one level. |
When you change activation levels, your scope changes. For example, unless you qualify a variable, as described in "Qualifying Names of Program Elements", dbx assumes that variables you reference are local to the current activation level. Also, dbx changes the current source file to the file containing the procedure's source.
Consider examining the stack trace for a program called test4 and moving up in the activation stack:
(dbx) where > 0 foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 1 foo(i = 4) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] 2 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test4.c":25, 0x10000fa0] 3 __start() ["/shamu/lib/libc/libc_64/csu/crt1text.s":137, 0x10000f34] (dbx) print i 5 (dbx) up foo: 40 r = foo2(i+1); |
The current activation level is now the procedure foo. As indicated in the output, the variable i receives the argument passed to foo and is therefore local to foo. The variable i at this activation level is different from the variable i in the foo2 activation level. You can reference the currently active i as "i"; whereas you must qualify the reference to the i in foo2:
(dbx) print i 4 (dbx) print foo2.i <symbol not found> |
Moving up one more activation level brings you to the main procedure:
(dbx) up main: 25 j = foo(j); (dbx) file /usr/var/tmp/dbx_examples/test4.c |
In this example, the source for main is in test4.c, whereas the source for foo and foo2 is in foo.c; therefore, dbx changes the current source file when you move up to the main activation level.
dbx resets the source file when you return to the foo2 activation level:
(dbx) down 2 foo2: 46 printf("foo2 arg is %d\n",i); (dbx) file /usr/var/tmp/dbx_examples/foo.c |
The func command moves you up or down the activation stack. You can specify the new activation level by providing either a procedure name or an activation level number.
The syntax for the func command is:
func {activation_level | procedure} |
| |
func | Displays the name of the procedure corresponding to the current activation level. |
When you change your activation level, your scope changes. For example, unless you qualify a variable as described in "Qualifying Names of Program Elements", dbx assumes that variables you reference are local to the current activation level. Also, dbx changes the current source file to the one containing the procedure's source and the current line to the first line of the procedure.
You can also give the func command the name of a procedure that is not on the activation stack, even when your program is not executing. In this case, dbx has no corresponding activation level to make current. However, dbx still changes the current source file to the one containing the procedure's source and the current line to the first line of the procedure.
For example, consider the following activation stack:
(dbx) where > 0 foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 1 foo(i = 4) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] 2 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test4.c":25, 0x10000fa0] 3 __start() ["/shamu/lib/libc/libc_64/csu/crt1text.s":137, 0x10000f34] |
In this case, you can go to the main activation stack by entering:
(dbx) func main main: 25 j = foo(j); |
This command changes the current activation level to "2" and changes the current source file to test4.c.
If you use the func command to go to a function that is not on the activation stack, dbx changes only the current source file to the one containing the procedure's source and the current line to the first line of the procedure:
(dbx) func bar 3 { (dbx) file /usr/var/tmp/dbx_examples/bar.c |
The dump command prints information about the variables in an activation level:
dump | Prints information about the variables in the current procedure. | |
dump procedure | Prints information about the variables in the specified procedure. The procedure must be active. Starts searching for procedure at the current activation level as set by the up or down command. (See "Moving Within the Stack" for more information about the up and down commands.) | |
dump . | Prints information about the variables in all procedures in all activation levels. |
For example, executing dump while in a function called foo2 appears as:
(dbx) dump foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] |
To examine the information for the procedure main, enter:
(dbx) dump main main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test4.c":25, 0x10000fa0] j = 4 i = 12 r = <expression or syntax error> a = 0 total = 0 |
To perform a complete dump of the program's active variables, enter:
(dbx) dump . > 0 foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 1 foo(i = 4) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] r = 0 2 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test4.c":25, 0x10000fa0] j = 4 i = 12 r = <bad operand> a = 0 total = 0 |
You can interactively call a function in your program from dbx.
If the function returns a value, you can use that function in a normal dbx expression. For example, consider a function prime defined in your program that accepts an integer value as an argument, and returns 1 if the value is prime and 0 if it is not. You can call this function interactively and print the results by entering a command such as:
(dbx) print prime(7) 1 |
If your function does not return a value, or if you want to execute a function primarily for its side effects, you can execute the function interactively with the dbx command ccall:
ccall func(arg1, arg2, ... , argn) |
|
Note: Structure and union arguments to a function, and structure and union returns from a function, are not supported. |
Functions called interactively honor breakpoints. Thus you can debug a function by setting breakpoints and then calling it interactively.
If you perform a stack trace using the where command while stopped in a routine executed interactively, dbx displays only those activation levels created by your interactive function call. The activation levels for your active program are effectively invisible. For example, a stack trace looks like this during an interactive function call:
(dbx) where > 0 foo2(i = 9) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 1 foo(i = 8) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] ===== interactive function call ===== 2 foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 3 foo(i = 4) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] 4 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test4.c":25, 0x10000fa0] 5 __start() ["/shamu/lib/libc/libc_64/csu/crt1text.s":137, 0x10000f34] |
If you stop execution of an interactively called function, you are responsible for eventually "unstacking" the call and returning from the function call. To unstack a call, you can complete the call using dbx commands such as cont, resume, next, or step as many times as necessary. If you run or rerun your program, dbx automatically unstacks all interactive function calls.
Another way to unstack an interactive function call is to execute the clearcalls command, which clears all stopped interactive calls.
(dbx) clearcalls |
When stopped or faulted within one or more nested interactive calls, the clearcalls command removes these calls from the stack and returns the program to its regular callstack. This command is useful when a segmentation fault, infinite loop, or other fatal error is encountered within the interactive call.
When stopped in an interactive call, the call stack displayed by where shows the following line at the end of each stack of interactive call instantiation.
==== interactive function call ==== |
For example, if the procedure foo() is interactively called from main(), you see the following stack:
> 0 foo2(i = 9) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 1 foo(i = 8) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] ===== interactive function call ===== 2 foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 3 foo(i = 4) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] 4 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/dbx_examples/test4.c":25, 0x10000fa0] 5 __start() ["/shamu/lib/libc/libc_64/csu/crt1text.s":137, 0x10000f34] |
You can also nest interactive function calls. In other words, if you have one or more breakpoints in a function, and you call that function repeatedly, each interactive call is stacked on top of the previous call. Breakpoints in a function affect all nesting levels, so you cannot have different breakpoints at different nesting levels.
The where command shows the entire stack trace from which you can determine the nesting depth. The following example has two nesting levels.
(dbx) where > 0 foo2(i = 17) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 1 foo(i = 16) ["/usr/var/tmp/src/dbx_examples/foo.c":40, 0x100011d4] ===== interactive function call ===== 2 foo2(i = 9) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 3 foo(i = 8) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] ===== interactive function call ===== 4 foo2(i = 5) ["/usr/var/tmp/dbx_examples/foo.c":46, 0x10001214] 5 foo(i = 4) ["/usr/var/tmp/dbx_examples/foo.c":40, 0x100011d4] 6 main(argc = 1, argv = 0xffffffad78) ["/usr/var/tmp/src/dbx_examples/test4.c":25, 0x10000fa0] 7 __start() ["/shamu/lib/libc/libc_64/csu/crt1text.s":137, 0x10000f34] |
To set a conditional breakpoint, for example, type:
(dbx) stop in foo if j == 7 Process 0: [3] stop in foo if j==7 |
If j is not within the scope of foo, then you will receive an error message if you attempt to call foo interactively. To prevent this, disable or delete any such breakpoints, conditional commands, or traces before executing the interactive function call.
The dbx command allows interactive control of a pixie instrumented binary.
pixie clear | Clear the basic block counts for the current execution. | |
pixie write | Write out the counts file with the current basic block counts. The counts reflect the execution of the program since the run command or since the last pixie clear command, whichever was more recent. |
When you debug a program that has been instrumented by pixie, it is often desirable to perform experiments over different code paths and do comparisons of the results. You can do this by capturing the pixie basic block counts at any point in the program's execution.
Suppose you want to determine the basic block counts for the section of code between lines 10 and 15 of a given file. Just set breakpoints at the two lines of interest, zero the counts when the first breakpoint is encountered, and then write out the counts file when the second breakpoint is encountered. For example:
(dbx) stop at "pix.c":15 Process 0: [3] stop at "pix.c":15 (dbx) stop at "pix.c":20 Process 0: [4] stop at "pix.c":20 (dbx) run Process 997 (pix.pixie) started [3] Process 997 (pix.pixie) stopped at [main:15 ,0x400a48 (pixie 0x404570)] 15 first = 12; (dbx) pixie clear (dbx) cont [4] Process 997 (pix.pixie) stopped at [main:20 ,0x400aa8 (pixie 0x404684)] 20 total = multiply(total, 2); (dbx) pixie write (dbx) sh prof -pixie prog -------------------------------------------------------------------------- Profile listing generated Tue Feb 14 11:08:46 1995 with: prof -pixie prog -------------------------------------------------------------------------- Total cycles Total Time Instructions Cycles/inst Clock Target 53 5.3e-07s 27 1.963 100.0MHz R4000 10: Total number of Load Instructions executed. 40: Total number of bytes loaded by the program. 3: Total number of Store Instructions executed. 12: Total number of bytes stored by the program. 2: Total number nops executed in branch delay slot. 0: Total number conditional branches executed. 0: Total number conditional branches actually taken. 0: Total number conditional branch likely executed. 0: Total number conditional branch likely actually taken. 18: Total cycles waiting for current instr to finish. 26: Total cycles lost to satisfy scheduling constraints. 5: Total cycles lost waiting for operands be available. *---------------------------------------------------------------------* -p[rocedures] using basic-block counts. * Sorted in descending order by the number of cycles executed in each * procedure. Unexecuted procedures are not listed. * *----------------------------------------------------------------------* cycles(%) cum % secs instrns calls procedure(file) 27(50.94) 50.94 0.00 19 1 main(prog:prog.c) 18(33.96) 84.91 0.00 4 1 multiply(prog:prog.c) 8(15.09) 100.00 0.00 4 2 add(prog:prog.c) |
The above example uses the sh command to invoke prof directly from dbx.
For an explanation of the above listing and information on the prof and pixie commands, see "Using the Performance Tools" in the Compiling and Performance Tuning Guide and the prof(1) and pixie(1) reference pages.
Debugging a program written in C++ is somewhat different from debugging programs written in other languages. This section describes features that affect how you access variables. See also the section in the following chapter, "Referring to C++ Functions."
Typically you use standard C++ syntax to access member variables of objects. For example, if the string _name is a member variable of the object myWindow, you can print its value by entering:
(dbx) print myWindow._name 0x1001dc1c = "MenuWindow" |
To display a static member variable for a C++ class, you must specify the variable with the class qualifier. For example, to print the value of the static member variable costPerShare of the class CoOp, enter:
(dbx) print CoOp::costPerShare 25.0 |