Duel is a special purpose language designed for concise state
exploration of debugged C programs, currently implemented for the GNU
gdb. Duel is invoked by entering the gdb command duel
(or dl
)
instead of print
:
(gdb) dl x[1..10] >? 5
x[3] = 14
x[8] = 6
prints the array elements x[1]
to x[10]
that are greater than 5.
The output includes the values 14 and 6, as well as their symbolic
representation "x[3]" and "x[8]".
Note that some gdb concepts (such as the value history) do not work
with the dl
command, and Duel expressions are not understood by
other gdb command.
Duel is based on expressions which return multiple values. The x..y
operator returns all integers from x
to y
; the x,y
operator
returns x
and then y
, e.g.
(gdb) dl (1,9,12..15,22)
prints 1, 9, 12, 13, 14, 15 and 22. Such expressions can be used wherever a single value is used, e.g.
(gdb) dl x[0..99]
prints first 100 elements of array x
.
Aliases are defined with x:=y
:
(gdb) dl if(x[i:=0..99]<0) x[i]
x[i] = -4
The symbolic output "x[i]" can be fixed by surrounding i
with curly
braces, i.e.
(gdb) dl if(x[i:=0..99]<0) x[{i}]
x[7] = -4
The curly braces are like parentheses, but they replace the symbolic representation of the argument with its value. You can usually avoid this altogether with direct Duel operators:
(gdb) dl x[..100] <? 0
x[7] = -4
The ..n
operator is a shorthand for 0..n-1
, that is, "first n
elements of the array". The x<?y
, x==?y
, x>=?y
, etc., 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.
Operators x.y
and x->y
allow an expression y
, evaluated under
x
scope:
(gdb) dl 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.
A useful alternative to loops is the x=>y
operator. It returns y
for each value of x
, setting _
to reference the value of x
, e.g.
(gdb) ..100 => if(emp[_].code>400) emp[_].code,emp[_].name
Using _
instead of i
also avoids the need for curly braces. When =>
operators are nested, __
refers to the parent scope value, ___
to the
great-parent, and so on.
Finally, the x-->y
operator expands lists and other data structures.
If head
points to a linked list threaded through the next
field, then:
(gdb) dl head-->next->data
head->data = 12
head->next->data = 14
head-->next[[2]]->data = 20
head-->next[[3]]->data = 26
produce the data field for each node in the list. x-->y
returns x
,
x->y
, x->y->y
, x->y->y->y
, etc. 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:
(gdb) head-->next[[3]]->data
head-->next[[3]]->data = 26
For example,
(gdb) dl head-->next[[50..60]]->data
return the 50th through the 60th elements in the list. By specifying a negative number one can count from the end:
(gdb) head-->next[[-1]]->data
head-->next[[-1]]->data = 123
And curly braces evaluate the offset, as usual:
(gdb) head-->next[[{-1}]]->data
head-->next[[73]]->data = 123
The #/x
operator counts the number of values, so
(gdb) dl #/( head-->next->data >? 50 )
counts the number of data elements over 50 on the list.
Operator x@y
stops x
generating values, as soon as y
becomes
true. It is mainly used with the unbounded x..
range:
(gdb) dl argv[0..]@(_ == 0)
argv[0] = 0x7fffffffe2bb "./a.out"
argv[1] = 0x7fffffffe2dd "--foo"
argv[2] = 0x7fffffffe300 "--bar"
argv[3] = 0x7fffffffe306 "42"
The @(_ == const)
is so common, that it can be abbreviated to
@const
:
(gdb) dl argv[0..]@0
Duel's semantics are modeled after the Icon programming language. The input consists of expressions which return sequences of values. C statements are expressions, too. Typically binary operators evaluate its operands and produce one result value for every combination of values from the left and right operands.
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
there are no other right operand value, 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:
(gdb) dl (5,3)+(6..8)
(5) + (6) = 11
(5) + (7) = 12
(5) + (8) = 13
(3) + (6) = 9
(3) + (7) = 10
(3) + (8) = 11
The computation for operators like x>?y
is similar, but when the
condition is false, the next values are fetched instead of returning a
value, reducing the sequence of x
values to those that satisfy the
condition. Operators like ..
return a sequence of values for each
pair of operands.
Duel types also follow the C semantics, with some important
differences. C types are checked statically; Duel types are checked
when operators are applied, e.g., (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
.
Command | Description |
---|---|
duel |
prints the list of commands |
dl |
alias for duel |
dl help |
prints short help |
dl ? |
alias for dl help |
dl longhelp |
prints this help file |
dl ?? |
alias for dl longhelp |
dl examples |
prints Examples section of this file |
dl operators |
prints Operators section of this file |
dl aliases |
prints the list of aliases, see x:=y operator below |
dl clear |
clears the list of aliases |
Variables and functions of the inferior (program being debugged) can be read
and called normally and work as expected. Convenience gdb variables
(like $a
) and references to results (like $1
) can be used too.
The complete list of operators, in the precedence order:
(x)
,{x}
- curly braces work as parentheses, but print the value ofx
, not its symbolic representation.x#y
- for every value ofx
, sety
to the index of thisx
value (0, 1, ...).x.y
,x->y
,x-->y
,x[y]
,x[[y]]
,x@y
- the first two operators are scope operators, they evaluatey
in the scope ofx
. The third one walks the linked list, evaluatingx
,x->y
,x->y->y
, etc until NULL. The fourth is a familiar C array element access, the fifth takes they
-th value in the sequence of values ofx
(counting from the end, ify
is negative). The last one stopsx
from generating values as soon asy
(evaluated in the scope ofx
) becomes true. Ify
is a literal, stops as soon asx
value becomes equal toy
. Ify
is a sequence of values, stops when at least one value in the sequence is true, and printsx
if at least one value in the sequence is false.(cast)x
,-x
,*x
,&x
,!x
,~x
,#/x
,+/x
,&&/x
,||/x
- first six are conventional unary C operators, the last four are grouping operators. The first one counts the numbers of values ofx
, the second sums them, the third returns a boolean AND of all values ofx
, the fourth - boolean OR. Just like in C, AND and OR operators are lazy and stop as soon as the result value is known.x/y
,x*y
,x%y
- conventional C operators.x-y
,x+y
- conventional C operators.x<<y
,x>>y
- conventional C operators.x..y
,..x
,x..
- ranges. First returns integers fromx
toy
, inclusive (x
can be greater thany
, for counting backwards). Second returns firstx
integers starting from 0 (in other words, it works like0..x-1
). Last returns an unlimited list of integers starting fromx
, and is normally used withx@y
.x<=y
,x>=y
,x<y
,x>y
,x<=?y
,x>=?y
,x<?y
,x>?y
- first four are conventional C operators, the second four returnx
if the condition is true, otherwise they return nothing (so, they work like a filter of thex
value sequence).x==y
,x!=y
,x==?y
,x!=?y
- same. First two are conventional C operators, the other two are filters.x&y
- conventional C operator.x^y
- conventional C operator.x|y
- conventional C operator.x&&y
- conventional C operator.x||y
- conventional C operator.x?y:z
- conventional C operator.x:=y
- create an aliasx
for the value ofy
. Note, that it is an alias toy
, not a copy ofy
value. Ify
value changes, the value of the alias will change too. Usedl aliases
anddl clear
commands to see and, respectively, clear the list of aliases.x,y
- creates a sequence of two values.x=>y
- for every value ofx
, store it in_
and evaluatey
.if (x) y else z
,if (x) y
- C statement turned into an operator. In the first form it's equivalent tox?y:z
, in the second form it has a filter semantics, returningy
for every non-zerox
(equivalent tox !=? 0 => y
).x;y
- evaluate x, ignoring all the results, then evaluate y.
Compute simple expression:
dl (0xff-0x12)*3
Display multiplication table:
dl (1..10)*(1..10)
Display x[i]
for the selected indexes:
dl x[10..20,22,24,40..60]
Display x[i]
backwards:
dl x[9..0]
Display x[i]
elements that are greater than 5:
dl x[..100] >? 5
Display x[i]
elements that are between 5 and 10:
dl x[..100] >? 5 <? 10
Same:
dl x[0..99]=>if(_>5 && _<10) _
Same, if x[i]
elements are integers:
dl x[..100] ==? 6..9
Display y[x[i]]
for each non-zero x[i]
:
dl y[x[..100] !=? 0]
Display both emp[i].code
and emp[i].name
for first 50 emp[]
elements
dl emp[..50].(code,name)
Display val[i].singl
or val[i].doubl
depending on val[i].is_doubl
:
dl val[..50].(is_doubl?doubl:singl)
Display hash[i].scope
for non-null hash[i]
:
dl (hash[..1024]!=?0)->scope
Check if x[i]
array is sorted:
dl x[i:=..100] >? x[i+1]
Check if x has non-unique elements:
dl x[i:=..100] ==? x[j:=..100] => if(i<j) x[{i,j}]
Same:
dl if(x[i:=..99] == x[j:=i+1..99]) x[{i,j}]
Find the first positive x[i]
dl (x[..100] >? 0)[[0]]
Find first five positive x[i]
dl (x[..100] >? 0)[[..5]]
Return the first x[i]
greater than 6 (note, no limit on i):
dl (x[0..] >? 6)[[0]]
Display argv[]
array until the first NULL:
dl argv[0..]@0
Display emp[]
values until emp[i].code
is 0:
dl emp[0..]@(code==0)
Walk the linked list by the next
pointer, starting from head
, print val
of each list element:
dl head-->next->val
Display the 21st element of a linked list:
dl head-->next[[20]]
Count elements on a linked list:
dl #/head-->next
Find the last element in a linked list:
dl x-->y[[#/x-->y - 1]]
Or just
dl x-->y[[-1]]
Walk the cyclic linked list (start from head
, walk until seeing head
again):
dl head-->(next!=?head)
Walk the binary tree:
dl root-->(left,right)->key
Select matching strings from the array:
dl $_regex(items[i:=0..20], "foo") ==? 1 => items[i]
Find first 10 primes greater than 1000:
dl (1000..=>if(&&/(2,3.._-1=>__%_ )) _)[[..10]]
Features of the original Duel, that are not implemented in the Duel.py yet:
- Builtins:
frame()
,sizeof()
,frames_no
,func.x
- Variables:
int i; i=5; i++
- Assignments:
x[..10]=0
while
andfor
operators
Features that were not in the original Duel:
- gdb scope specification:
file.c::var
- gdb convenience variables (
$a
), references to results ($1
) and functions ($_streq
) - negative indexes in
[[ ]]
operator
Duel was designed by Michael Golan as part of a PhD thesis in the Computer Science Department of Princeton University. He also implemented Duel in C as a patch for gdb 4.x.
Duel.py is a pure-python implementation written by Sergei Golubchik
Duel stands for Debugging U (might) Even Like, or Don't Use this Exotic Language. Judge for yourself!