Ziad Al-Sharif
July , 2008
Unicon Technical Report #10
Abstract
![]() |
http://unicon.sourceforge.net/utr/utr10.html Department of Computer Science University of Idaho Moscow, ID 83843 |
1. About UDB
UDB is a source-level debugger built on top of a monitoring framework
called the Alamo framework [1]. Alamo stands for A Lightweight Architecture for
MOnitoring; it is built into Unicon's virtual machine [2]. The main goal of
UDB is to allow you to see what is going on inside your program during its
execution or to see what your program was doing when it crashed. You can use
UDB to debug programs written in Icon [3] and Unicon [2].
UDB features the classical debugging techniques found in a conventional
debugger such as GDB [4]. At the same time, it has a rich set of advanced
debugging techniques; one of those advanced techniques is an extensive
execution behavior tracer. UDB also utilize some automatic debugging
techniques to catch some predefined set of elusive bugs.
In UDB, you can run your program.
If it is ever stopped, you will be able to examine what has happened,
and you may change things to fix the problem or you may specify anything
that might affect its behavior. Furthermore, you can use UDB's tracing
facilities to comprehend different behavioral aspects of the
execution, you can trace the entire execution or just any portion of it that
starts and ends on specified condition. The automated techniques for
catching some predefined situation, where it is possibly a bug, is huge
advancement toward automating the debugging process.
2.
Getting In and Out of UDB
Once
UDB is started, it provides a console as an interface between you and the
debugging session until you exit it.
2.1. Invoking UDB
To invoke UDB, run the
program udb in one of the following formats:
udb
Starts a UDB session without any loaded program, then if
you want to load or reload a program into udb after udb is already
started, the
command "load" must be used.
udb program
Starts a UDB session and loads the executable named "program" at the same time.
udb program arguments
Starts a UDB session and loads the
executable named "program" into the
udb session, that
executable "program" can make use of the argument list "arguments" as
parameters to that program.
2.2. Quitting UDB
To exit UDB type the command
quit at any point during the debugging session.
quit
q
Exits UDB, if there is any program is running, a
question will show up asking about your permission to quit UDB, if
you are sure to exit, jut type "y", otherwise type "n" to ignore the
quit command.
3. The Console
UDB console provides an
interface between you and the
udb session. You can repeat any UDB command by pressing just
ENTER. You
can also use the UP/DOWN keys to get UDB to find you a command from the
list of the current session command history .
3.1. Command syntax
UDB commands can
be formed in a single line of input. A command usually starts with a command
name, then followed by zero or more arguments.
Arguments are based on the command itself, each command has different
set of possible arguments. A blank line as input to UDB means to repeat
the previous command.
3.2. Getting help
You can always ask UDB itself for information on its commands, using the
command help.
help
h
Prints a list of all possible commands in the current debugging
situation, the list includes a brief description of each command.
help command
h
command
Prints a complete description of the command "command" and its
possible
arguments.
4.
Running Programs Under UDB
When you run a program under UDB, no
recompilation of the program is needed. You just load your program under
UDB as follows:
4.1. Loading your program
You may
load your program when you start UDB. If
you did not, from within UDB you can load/reload your
program, at any time, using one of the following ways:
load program
Loads the executable program named "program"
into the UDB session.
load program
arguments
Loads the executable program named "program" into the
UDB session and passes the argument list "arguments" into that loaded program.
4.2. Starting your program
Use the
run command to start your program under UDB. You may specify the
argument list, if any. If you already pre-specified the argument list
when you loaded the program, then you do not have to re-specify it,
unless you want to re-define it or to run the program with different
arguments.
run
r
Runs the loaded program under UDB. It can be used also to re-run
the program at any time during the UDB session.
run arguments
r arguments
Runs the loaded program with
the argument list "arguments", if the program
is running or was running previously, then it will re-run the
program with this new argument list "arguments".
4.3. Your program's input and output
(Not implemented yet)
By default, UDB switches the terminal to its own terminal modes to
interact with you. The program you run under UDB does its input and output
to the same terminal that UDB is using. In the current implementation,
UDB supports no way to redirect your program's input and/or
output using the shell redirection with the run command.
5.
Stopping and Continuing
One of the main goals of a source-level debugger is that you can stop
your program before it terminates. While your program is running under
UDB, it may stop if it reaches a break point, a watch point, or a new
line after the UDB's step or next commands. Stopping the program in the
middle of the execution allows you to investigate any trouble and find its
actual cause. You may use any of the UDB commands to
examine and/or change the state of your program, or even set and/or
remove break points or watch points before you continue the execution.
5.1. Setting break points
Break points are places where you like the program to stop its
execution so you can investigate.
break
b
Prints out all of the
break points that were set previously.
break
linenum
b linenum
Checks if there is a code in that line number "linenum", if yes then it sets
a break point in that line, if no source code, then it prints out an error
message.
break filename linenum
b
filename linenum
Sets a break point on the line number "linenum" in that specified filename
"filename".
break procedure
b procedure
Searches in the source code for that procedure
named "procedure". If there is one, it sets a break
point on the line number of the procedure entrance, otherwise, it prints out an
error message.
5.2. Setting watch points
Watch points
enable you to stop the execution whenever
some expression is evaluated or a variable changes its value.
watch
Prints a list of all the predefined set of watched expressions
and/or variables
watch variable
If the program
is loaded but it is not running yet, the semantic of this command
assumes that the variable "variable" is one of the global variables,
else if the program is already running and it is been stopped for a
legitimate reason, then the semantic of this command
assumes that the variable "variable" is a local variable in the current
procedure or current stack frame. In both cases, UDB will break
at every line that the variable "variable" is evaluated and changed its
value.
watch variable procedure
Breaks at every line that the
local variable "variable" in the specific procedure "procedure" is
evaluated and changed its value. The semantic of this command assumes that
the variable "variable" is
local to that procedure named "procedure" regardless if the program is running or
not.
5.3. Deleting break points
The delete command deletes
an individual break point by
specifying its line number or its procedure name.
delete linenum
Deletes the break point that is already on that line number
"linenum"
delete procedure
Deletes the break point that is already
on that procedure named "procedure"
5.4. Stepping and continuing
Stepping using the command "step" means executing just one line of your program source code.
Stepping using the command "next" means continue to the next source
line in the current stack frame; "next" is similar to "step"
but without going inside any procedures or function calls that
appear within the source code. In "next", execution stops when control
reaches a different line of source code at the original stack level that
the control was in when you gave the "next" command. Continuing using the
"continue" command means resuming program execution until your
program completes normally or reaches another break point or watch
point.
step
s
Steps one source line in the execution of the program
step count
s count
Steps
a number of lines, in the
source program, that are equal to "count". The default value of "count" is one.
next
n
Steps one source line in
the source program. If that source line has any procedure or function
call, the control
will not step anywhere inside that call, and the called procedure or function will be evaluated without
showing any stepping inside.
next count
n
count
Steps
a number of source lines that is equal to "count". Stepping each line
without showing any control detail for any procedure or function call
that may occur in that source line. Each source line will be evaluated
as one line without showing any steps of control inside any call from
that line.
continue
cont
c
Resumes the program's execution
at
its normal speed until it terminates or reaches a break point or a watch point.
6. Examining the Stack
When a
program performs a procedure call, information about the call is generated
and saved in the execution stack in a block called the procedure frame. Each
frame includes the level, the arguments, and the local variables of the
called procedure. The procedure frame is saved on the stack until the procedure is
returned. However, when the program is stopped, you may need to know where
and/or how it got there; UDB commands help you do that.
By default, when the program stops,
UDB points implicitly to the
current procedure frame, which is the last frame on the execution stack. However, you
can explicitly jump to any frame by making the current frame whichever frame
number
you are interested in. This is important because there is some UDB commands
refer implicitly to the current selected frame. In particular, whenever you
ask UDB for the value of a variable, the variable is supposed to
be found in the current selected frame.
6.1. Information about a frame
You can obtain information, about the current state of the execution
stack and its frames, using one of the following commands:
frame
f
Prints a brief description of the
current selected stack frame plus a list of the global variables,
local variables, and the formal parameters. When it is used without
any argument, this command does not change the current selected frame.
frame n (not implemented yet)
f n
Prints a brief description of the frame number "n". By default the
current farm is fame number 0, which is the innermost, the oldest frame
on the the stack has the biggest frame number, which is the frame for
main. This command does change the current selected frame by making
it pointing at the frame number "n".
level
Prints out the number of levels
(number of frames) that are in the execution stack.
6.2. Selecting a frame
You can select
a frame from the current execution stack using one of the following
commands:
up
Moves the current frame pointer one frame up in the execution stack.
Assuming the stack is growing downward, if the current frame pointer
points at the very
oldest frame in the stack, then there is no more frames to go up in
the stack.
up n
Moves the current frame pointer
"n" number of frames up in the execution stack. The default value of "n"
is one.
down
Moves the current frame pointer one frame down on the execution
stack. Assuming the stack is growing downward, if the current frame
pointer points at the very
top frame in the stack, then there is no more frames to go down in
the stack.
down n
Moves the current frame pointer
"n" number of frames down in the execution stack. The default value of
"n" is one.
6.3. Back tracing
Back tracing allows you to
investigate the entire execution stack. It is commonly used after a runtime error. Another synonym for this command is
"where"
backtrace
bt
Prints out
information about every procedure frame in the current execution stack.
backtrace n (Not implemented yet)
bt
n
Prints out information about the "n" innermost
procedure frames from the current frame pointer.
backtrace -n (Not implemented yet)
bt
-n
Prints out information about the "n" outermost
procedure frames from the current frame pointer.
7. Examining Data
The print command
is used to either get a peek at one of the
variables or change the value of a variable.
7.1. Getting a peek
at the value of a variable
Usually, the print command is used. It
assumes that the variable is in the current stack frame. The printed
value of the variable is different based on the variable type, if
the type of the variable is an Atomic Type, then the printed value is
its current actual value. However, if the variable's type is a
Structured Type, then the printed value is an image or ximage of that
variable. Hence, the print command can be used to print an element or
field of a structure such as list, table, or record, when the variable
is a string, then you can print the
whole string or just a substring.
To print an element of a list, you just refer to that element in
the source code, for example:
print L[2] prints out the value of the element
L[2]
print L[2][10] prints out the value of the element
L[2][10]
print L[i][j] prints out the value of the element L[i][j]
after evaluating both of i and
j, note that i and
j must be
valid variables in the current context.
To print an element of a table, it is very similar to the list subscription with the
extension that tables are not restricted to the integer keys.
To print a string value or a substring, you just refer to that element
in the source code, for example:
print S prints the value of
S
print S[5] prints the character in the position
5 in the
string S
print S[2:8] prints all the characters between the position
2 and 8 of the string
S
To print any field of a record, you just refer to that field
using the dot operator, for example
print r.f prints out the value of the field
f of that record
r
Here is a detailed description of the print command:
print
p
Prints a snapshot of all of the
global and local
variables of the current active procedure that you are stepping in,
it also includes the formal parameters of that
procedure.
print variable
p
variable
Prints out the value of the variable "variable".
The passed variable named "variable" is assumed
to be local to the current procedure frame that you are stepping in, if not it
will be searched within the global variables.
print variable n
p variable
n
Prints out the value of variable "variable" in the stack frame number
n.
Frame 0 is the
innermost frame, frame 1 is the direct parent of the innermost
frame, frame n is the frame number
n away from the current frame, which is
referred to the procedure main.
print -G
p -G
Prints a
snapshot of all of the global variables and their values at that current program
state.
print -L
p -L
Prints a
snapshot of all of the variables and their values that are local to that current procedure
frame that you are stepping in.
print -P
p -P
Prints a snapshot of all of the parameters and their values that are passed to
the current procedure frame that you are stepping in.
print keyword
p keyword
Prints out the value or an image of any valid Unicon keyword
"keyword"
7.2. Changing the value of a variable
The
same print command that is used to get a peek at a variable, it can be
used also to change the value of a variable. In order to change a
variable value in your program from within UDB, the assignment operator ":="
or "=" must be
used.
print variable := value
p
variable := value
It evaluates the right hand side of the
assignment "value" and assigns the result to the variable "variable"
in the left hand side of the assign operator. The "value" can be a
literal value or any other valid variable in the current program context.
The
variable "variable" is assumed to be in the current
active procedure frame, otherwise it will be looked up in the global variables.
The variable "variable" can be a simple variable, an element of a
structured variable, or any other valid Unicon variable. If the
variable "variable" is an element of a structure such as:
A list:
You can assign a an element of a list by referring to that
element in the source code, for example:
print L[2]:=3 assigns
L[2] with the integer literal
3
print L[5]:=b assigns
L[5] with the value of the variable
b
print L[i][j]:=10 assigns the element
L[i][j] with the integer
literal 10, both
i &
j must be valid variables in the
current context.
A table:
Assigning to an element of a table is similar to assigning to an
element of a list, the only the difference is that a table
key is not limited to the integer keys.
A string:
You can assign to a string variable or to a substring, for example: if S is variable that holds a string value then it is valid to say:
print S := "abcdefg"
or
print S[1]
:= "A" or
print S[2:6]
:= "ab"
A record:
You can assign any record field using the dot
operator, for example:
print r.f := 10 assigns the field member f of the record r the
integer literal 10
A valid Unicon keyword:
print &keyword := value
p
&keyword := value
It changes the keyword value into the
new assigned value.
Not: this type of assignment to a keyword
depends on the keyword itself, in Unicon not all the keywords are variables that
you can assign to.
8. Examining Source Files
UDB knows about the source code and what files are involved in building
the executable. At the load time, UDB tries to open all of the related
source files including the library files, it builds a list of what source
files are available and what are not. It reads such information from the
icode of the executable itself. UDB can print parts of your program's
source, when your program reaches a break point or a watch point, UDB
spontaneously prints to the line where it stopped, likewise, when you back
trace or select a stack frame, UDB prints information about the file and the
line where the execution in that frame has stopped. In general, you can
print any part of a source file or query about what files are successfully
opened and what are not.
8.1. Printing source lines
The command
"list" allows
you to print lines from the source code. The default number of lines are
ten, and the default source file is the current source file that the program is stopped in.
If the program is not running yet, the current file is the file that has
the procedure main( ) in it.
list linenum
l
linenum
Prints out ten lines centered around the line
number "linenum"
list procedure
l procedure
Prints out ten lines centered around the header of the
procedure "procedure"
list
l
Prints out ten lines from the current source file centered around the
current line. Another list command will print out the next ten lines in
the source file.
list -
l -
Print out the previous ten lines.
8.2. Found and missing source
files
The src command allows you to check what source files are
found and what are missing for any reason
src -f
Prints a list of all the file names that are found and
opened by udb, those are the source files that can be navigated with the list command.
src -m
Prints a list of all the file names that are missing
and not opened by udb. Even though those files are missing, udb is
still able to work, the only degradation in the features is that you can not navigate the source
code in the missing file.
src
Prints all of the file names that are used to build the executable,
that include all used files from libraries, packages, and the user
files. It separates the found files from the missing ones.
9. Tracing
No one can ignore the benefits from pausing the execution and investigating the
state of the program, but it is not always enough or even a valid choice. You
may be interested in understanding the behavior of the execution
over a period of time, or may be the program depends on some real-time
behavior, where stopping the execution might cause the program to change its
behavior. UDB provides tracing facilities that allow you to depict the state
and/or the behavior of the program without pausing the execution. However,
tracing the execution can generate huge amount of data in a very short period of
time, just remember to be selective about what to trace and to trace small
pieces of the program instead of tracing the whole program. UDB's tracing facilities are divided into two
main categories.
9.1. Tracing a variable
UDB allows
you to trace one or more variables during the execution
of the program. The tracer can be enabled and/or disabled at any point of the
program's execution. It traces every value that the traced variable ever
had combined with information about the file name, the line number, and the
procedure name. You do not have to wait until the execution is complete in order
to query about the traced information, you can investigate the traced
information at any point of the execution.
trace variable
If this command is applied while the program
is stopped for any reason, the tracer considers that variable
"variable" as local variable in the current procedure, otherwise the tracer
assumes that the variable "variable"
is global and traces it during the rest of the execution. It sets a trace point
on that variable "variable" to trace every value
assigned to it combined with information consists of the file name, the line
number, and the procedure name where that value is assigned.
trace variable procedure
This exactly same as "trace variable" command but the tracer here does not
have to guess the scope of the traced variable "variable" since the procedure is
provided in the command. It sets a trace point on that variable "variable"
to trace every value assigned to it combined with information consists of the
file name, the line number, and the procedure name where that value is assigned.
enable
-trace
variable
If this command is applied while the program
is stopped for any reason, the tracer considers that variable
"variable" as local variable in current procedure, otherwise the tracer
assumes that the variable "variable"
is global and traces it during the rest of the execution. Enables the tracer on that variable
"variable", if that was at any break point
during the program execution; the tracer will assume the variable
"variable" is local to
the current procedure that the break point is on, otherwise it will
assume the "variable" is a global variable and trace it in the whole program.
enable -trace variable procedure
This exactly same as "enable trace variable"
command but the tracer here does not have to guess the scope of the
traced variable since the procedure is provided in the command. It
enables the tracer on that local variable "variable" in the provided
procedure "procedure". This command can be applied at any point of
the program execution.
disable -trace variable
If this command is applied while the program
is stopped for any reason, the tracer considers that variable
"variable" as local variable in current procedure, otherwise the tracer
assumes that the variable "variable"
is global and traces it during the rest of the execution. Disables the tracer on that variable "variable", if that was at any break point
during the program execution; the tracer will assume the variable
"variable" is local to the current
procedure that the break point is on, otherwise it will assume the "variable"
is a global variable and stops the trace in the whole program.
disable -trace variable procedure
This exactly same as "disable trace variable" command but the tracer here does
not have to guess the scope of the traced variable since the procedure
is provided in the command. It disables the tracer on that local
variable "variable" in the provided procedure "procedure". This command
can be applied at any point of the program execution. This exactly same
as "disable trace variable" command but
the tracer here does not have to guess the scope of the traced variable
since the procedure is provided in the command.
info -trace variable
If this command is applied while the program
is stopped for any reason, the tracer considers that variable
"variable" as local variable in current procedure, otherwise the tracer
assumes that the variable "variable"
is global and traces it during the rest of the execution. It prints out a list
of all the traced
information about the variable "variable".
The information consist of an image of every value assigned to that variable "variable"
combined with the file names, the line numbers, and the procedure names.
info -trace variable procedure
It prints out a list of all the traced
information about the variable "variable".
The information consist of an image of every value assigned to that variable "variable"
combined with the file names, the line numbers, and the procedure names.
9.2. Tracing an execution
behavior
Dynamic tracing is an important feature to
understand the execution of a running program; it provides the user with
information about what really happens during the execution. That is what drove
an operating system such as Solaris to build DTrace [6] in order to help its
developers understand the behavior of their program. DTrace is used to observe,
debug and tune system behavior. It is built into the operating system, and
complements other forms of application behavior tracing. UDB's tracing approach
resembles a more detailed version of GDB's tracing facility, which means that the
user can trace and debug in the same session, and the trace output can be
customized based on the user interest. It allows you to trace some of the execution behaviors such as
procedure calls, operator failures, and so many other features.
9.2.1 Tracing procedure activities
Tracing procedures is useful when the user wants to learn about the
behavior, the sequence, and/or the values of the passed parameters
at each procedure activities such as call,
return, suspend, fail, and resume. The traced procedures can overcome
the limitations of the execution stack. The execution stack provides you with
information about those live procedures in that current execution state,
however, tracing procedures can provide you with information not only about
those live procedures but also about those procedures that maybe returned just
before pausing the execution. Furthermore, investigating the execution stack
does not tell whether that live procedure in the stack is suspended or just
called another procedure. Traced procedures can assist the stack investigation
with detailed information.
trace
-proc
Traces all of the procedure
activities such as call,
return, fail, suspend, and resume. Traced information composed of the
file name, the line number, the stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
trace -proc procedure
Traces only the activities of the procedure named "procedure",
the traced activities are the procedure call,
return, fail, suspend, and resume. Traced information composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot their values at that specific activity.
trace -pcall
trace -preturn
trace
-pfail
trace -psuspend
trace -presume
Traces only one activity for all of the procedures such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
trace -pcall procedure
trace
-preturn procedure
trace -pfail procedure
trace -psuspend procedure
trace -presume
procedure
Traces only the procedure named "procedure"
and only one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
enable -trace -proc
Enables
the tracer at all of the procedure activities such as call,
return, fail, suspend, and resume. Traced information composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
enable -trace -proc procedure
Enables the tracer only for the activities of the procedure named "procedure",
the traced activities are the procedure calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and and a snapshot of their values at that specific activity.
enable -trace -pcall
enable -trace -preturn
enable
-trace
-pfail
enable -trace -psuspend
enable -trace -presume
Enables the tracer only with one activity for all of the procedures
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
filename, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot their values at that specific activity.
enable -trace -pcall procedure
enable
-trace
-preturn procedure
enable -trace -pfail procedure
enable -trace -psuspend procedure
enable -trace -presume
procedure
Enables the tracer only for the procedure named "procedure"
and only with one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and and a snapshot of their values at that specific activity.
disable -trace -proc
Disables the tracer at all of the procedure activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
disable trace -proc procedure
Disables the tracer only for the activities of the procedure named "procedure",
the traced activities are the procedure call,
return, suspend, fail, and resume. Traced information composed of the
filename, line number, execution stack level, procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
disable -trace -pcall
disable -trace -preturn
disable
-trace
-pfail
disable -trace -psuspend
disable -trace -presume
Disables the tracer only with one activity for all of the procedures
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
disable -trace -pcall procedure
disable
-trace
-preturn procedure
disable -trace -pfail procedure
disable -trace -psuspend procedure
disable trace -presume
procedure
Disables the tracer only for the procedure named "procedure"
and only with one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
info -trace -proc
Prints
out the traced information for all of procedures and all of their
activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
info -trace -proc procedure
Prints out only the traced information of the procedure named "procedure", the traced activities are the procedure calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
info -trace -pcall
info -trace -preturn
info
-trace
-pfail
info -trace -psuspend
info -trace -presume
Prints out the traced information for only one activity for all of the procedures
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot of their values at that specific activity.
info -trace -pcall procedure
info -trace
-preturn procedure
info -trace -pfail procedure
info -trace -psuspend procedure
info -trace -presume
procedure
Prints out the traced information only for the procedure named "procedure"
and only one traced activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, the procedure name, and the formal parameters names
and a snapshot their values at that specific activity.
9.2.2. Tracing built-in function activities
UDB's tracing facilities goes places other tracers might not be able to
trace, one of those places is the built-in functions which are
supported by the Unicon's runtime system. Built-in functions provide a
tremendous support for the Unicon programmer who uses them all the time. Tracing
built-in function's activities gives the user a fine level of details about the execution
behavior of the subject program. Those built-in functions activities,
such as calls, returns, fails, suspends, and resumes, can be crucial to the
behavior of the running program and understanding their behavior can be crucial
to understanding the behavior of the running program.
trace -func
Traces all of the
built-in functions activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
trace -func function
Traces only the activities of the function named "function",
the traced activities are the function calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function
name.
trace -fcall
trace -freturn
trace
-ffail
trace -fsuspend
trace -fresume
Traces only one activity for all of the
built-in functions such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function
name.
trace -fcall function
trace -freturn
function
trace -ffail function
trace -fsuspend function
trace -fresume
function
Traces only the built-in function named "function"
and only one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function
name.
enable -trace -func
Enables the tracer at all of the
built-in functions activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
enable -trace -func function
Enables the tracer only for the activities of the
built-in function named "function",
the traced activities are the function calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function
name.
enable -trace -fcall
enable -trace -freturn
enable
-trace
-ffail
enable -trace -fsuspend
enable -trace -fresume
Enables the tracer only with one activity for all of the
built-in functions activities
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
enable -trace -fcall function
enable -trace
-freturn function
enable -trace -ffail function
enable -trace -fsuspend function
enable -trace -fresume
function
Enables the tracer only for the
built-in function named "function",
the traced activity is all of the calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
disable -trace -func
Disables the tracer at all of the
built-in functions activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
disable -trace -func function
Disables the tracer only for the activities of the
built-in function named "function",
the traced activities are the function calls,
returns, suspends, fails, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function
name.
disable -trace -fcall
disable -trace -freturn
disable
-trace
-ffail
disable -trace -fsuspend
disable -trace -fresume
Disables the tracer only with one activity for all of the
built-in functions
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
disable -trace -fcall function
disable
-trace
-freturn function
disable -trace -ffail function
disable -trace -fsuspend function
disable -trace -fresume
function
Disables the tracer only for the
built-in function named "function"
and only with one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
info -trace -func
Prints out the traced information for all of
built-in functions and all of
their activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
info -trace -func function
Prints out only the traced information of the
built-in function named "function"
and all of its activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
info -trace -fcall
info -trace -freturn
info
-trace
-ffail
info -trace -fsuspend
info -trace -fresume
Prints out the traced information for only one activity for all of the
built-in functions
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
info -trace -fcall function
info -trace
-freturn function
info -trace -ffail function
info -trace -fsuspend function
info -trace -fresume
function
Prints out the traced information only for the function named
"function" and only one traced
activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
9.2.3. Tracing built-in operation activities
UDB's tracing facilities goes places other tracers might not be able to
trace, beside the built-in functions, it allows tracing the built-in
operations which are
supported by the Unicon's runtime system, they in the Unicon programmer all the time,
and they have the semantic of the Unicon such as calls, returns, fails,
suspends, and resumes. The semantic of those built-in operations gives the
ability to trace them a significant importance by providing the user with
another fine level of details about the behavior of the subject program.
trace -Oper
Traces all of the
built-in operations activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation name.
trace -Oper Operation
Traces only the activities of the
built-in operation named "Operation",
the traced activities are the operation calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and operation name.
trace -Ocall
trace -Oreturn
trace
-Ofail
trace -Osuspend
trace -Oresume
Traces only one activity for all of the
built-in operations such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation
name.
trace -Ocall Operation
trace
-Oreturn Operation
trace -Ofail
Operation
trace
-Osuspend Operation
trace -Oresume
Operation
Traces only the built-in
operation named "Operation"
and only one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation
name.
enable -trace -Oper
Enables the tracer at all of the
built-in operations activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation name.
enable -trace -Oper Operation
Enables the tracer only for the activities of the
built-in operation named "Operation",
the traced activities are the operation calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation
name.
enable -trace -Ocall
enable -trace -Oreturn
enable
-trace
-Ofail
enable -trace -Osuspend
enable -trace -Oresume
Enables the tracer only with one activity for all of the
built-in operations
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, ad operation
name.
enable -trace -Ocall Operation
enable
-trace
-Oreturn Operation
enable -trace -Ofail
Operation
enable
-trace
-Osuspend Operation
enable -trace -Oresume
Operation
Enables the tracer only for the
built-in operation named "Operation"
and only with one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation name.
disable -trace -Oper
Disables the tracer at all of the
built-in operations activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation
name.
disable -trace -Oper Operation
Disables the tracer only for the activities of the
built-in operation named "Operation",
the traced activities are the operation's calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation
name.
disable -trace -Ocall
disable -trace -Oreturn
disable
-trace
-Ofail
disable -trace -Osuspend
disable -trace -Oresume
Disables the tracer only with one activity for all of the
built-in operations
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation
name.
disable -trace -Ocall Operation
disable
-trace
-Oreturn Operation
disable -trace -Ofail
Operation
disable
-trace
-Osuspend Operation
disable -trace -Oresume
Operation
Disables the tracer only for the
built-in operation named "Operation"
and only with one activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation
name.
info -trace -Oper
Prints out the traced information for all of
built-in operations and all of
their activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation
name.
info -trace -Oper
Operation
Prints out only the traced information of the
built-in operation named "Operation", the traced activities are the
operation calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the operation
name.
info -trace -Ocall
info -trace -Oreturn
info
-trace
-Ofail
info -trace -Osuspend
info -trace -Oresume
Prints out the traced information for only one activity for all of the
built-in operations
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation
name.
info -trace -Ocall Operation
info -trace
-Oreturn Operation
info -trace -Ofail
Operation
info -trace
-Osuspend Operation
info -trace -Oresume
Operation
Prints out the traced information only for the
built-in operation named
"Operation" and only one traced
activity such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the operation
name.
9.3.4. Tracing string scanning
activities
UDB's tracing supports the string scanning activities. In
a language such as Unicon invest heavily in the string scanning operations, it
is very important to provide the user with the ability to
trace them and give the user to investigate the state and the activities
used during the string scanning.
trace -Scan
Traces all of the
string scanning activities such as started new scan, suspend scan, failed
scan, resumed scan, and returned scan . Traced information composed of the
file name, the line number, the execution stack level, and the function
name.
trace -Snew
trace -Sreturn
trace -Sfail
trace -Ssuspend
trace -Sresume
Traces only one activity for all of the
string scanning operations such as a new scan, returned scan, failed
scan, suspend scan, and resumed
scan respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function
name.
enable -trace -Scan
Enables the tracer at all of the
string scanning operations and activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
enable -trace -Snew
enable -trace -Sreturn
enable
-trace
-Sfail
enable -trace -Ssuspend
enable -trace -Sresume
Enables the tracer only with one activity for all of the
string scanning operations
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
disable trace -Scan
Disables the tracer at all of the
string scanning operations and their activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
disable -trace -Snew
disable -trace -Sreturn
disable
-trace
-Sfail
disable -trace -Ssuspend
disable -trace -Sresume
Disables the tracer only with one
activity for all of the string scanning operations
such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the name.
info -trace -Scan
Prints out the traced information for all of
the string scanning operations and all of
their activities such as calls,
returns, fails, suspends, and resumes. Traced information composed of the
file name, the line number, the execution stack level, and the function name.
info -trace -Snew
info -trace -Sreturn
info
-trace
-Sfail
info -trace -Ssuspend
info -trace -Sresume
Prints out the traced information for only one activity for all of the
string scanning operations such as calls,
returns, fails, suspends, and resumes respectively. Traced information
composed of the
file name, the line number, the execution stack level, and the function name.
10. Extensible Debugging
UDB provides an open debugging architecture that is capable of being cooperative
with external debugging tools. GDB style of debugging is not always good enough,
the nature of the bug or the debugging situation may need some help from
external or internal tools that would provide information beyond what a break
point debugger is capable of. Fortunately, UDB has an open architecture that
permits external or internal event-based monitoring tools to be loaded and
enabled/disabled during the debugging session. Such tools might vary in its
goals. some may trace specific event while others may dedicate to capture a
specific execution behavior that might be hard or impossible to be captured
using the break points, watch points, and stepping/continuing strategies.
10.1 Internal monitors
UDB has a library of different monitors, which serve different behaviors such as
memory allocations, garbage collections, failed loops, failed subscripts,
variable profiles, dead variables, loops time, procedure times, etc. Internal
monitors are disabled by default, in order to be used, the user has to enable
them explicitly.
10.1.1 Enabling internal monitor
enable -internal monitor
Enables the internal monitor named "monitor",
the command is valid from any point during the debugging session.
10.1.2 Disabling internal monitor
disable -internal monitor
Disables the internal monitor named "monitor",
the command is valid from any point during the debugging session. The only
condition is that the disabled monitor must be enabled previously.
10.1.3 Analyze
information in the internal monitor
analyze -internal monitor
Analyzes the information collected by the internal monitor named "monitor",
this command is valid after enabling the internal monitor from any point during the debugging session.
However, some internal monitors may not have this facility; for such monitors,
this command will fail silently.
10.1.4 Print
information from the internal monitor
print -internal monitor
Prints the information collected and analyzed by the internal monitor named "monitor",
the command is valid after enabling the internal monitor from any point during
the debugging session. However, some internal monitors may not have this
facility; for such monitors, this command will fail silently.
10.1.5 Information about internal monitors
info -internal monitor
Provides the current state of the internal monitor named "monitor",
whether is is disabled or enabled. The command is valid from any point during
the debugging session.
info -internal
Provides the current state of all internal monitors that are available in the
system and whether each one is enabled or disabled. The command is valid from
any point during the debugging session.
10.2 External monitors
UDB's extensible architecture opens the door for compatible stand-alone monitors
to be loaded and used on the fly during the debugging session. Any Alamo-based
stand-alone event-driven monitor can be loaded under UDB's control. Loaded
external monitors are enabled by default from the point of loading during the
debugging session. external monitors will be paused when the debuggee program is
paused and resumed when the debuggee program is resumed after a break point or
watch point, or even between stepping and continuing.
10.2.1 Loading external monitors
load -external monitor
Loads the external stand-alone monitor named "monitor"
and enables it by default. The command is valid from any point during the
debugging session.
10.1.2 Enabling external monitor
enable -external monitor
Enables the external monitor named "monitor",
the command is valid from any point during the debugging session. The only
condition is that the enabled external monitor must be previously loaded and
disabled.
10.1.3 Disabling external monitor
disable -external monitor
Disables the external monitor named "monitor",
the command is valid from any point during the debugging session. The only
condition is that the disabled monitor must be loaded previously.
10.1.4 Information about external monitors
info -external monitor
Provides the current state of the external monitor named "monitor",
whether is is disabled or enabled. The command is valid from any point during
the debugging session.
info -external
Provides the current state of all loaded external monitors that are available in
the system and whether each one is enabled or disabled. The command is valid
from any point during the debugging session.
10.3 Migration form externals to internals
External monitors impose an extra level of communication overhead in the form of
co-expression context switch (Unicon light weight of threads are called
co-expression). for better performance external tools can be migrated to
internals. UDB's extensible architecture is open for stand-alone external tools
that are:
1) Alamo event-based monitors.
2) Written in either Icon or Unicon.
10.3.1 Simple monitor
The following is a very simple Alamo based monitor, where each event is mapped,
with one-to-one relation, into a single method. This format allows UDB to
provide automatic and easy
registration for the used methods and their event mask. All you needs to do is
to derive this simple monitor from the Listener()
class provided by the UDB's open architecture, then using the
registration function which is:
register("name",
object);
where the first parameter is providing a formal name to the
monitor and the second parameter is the actual object of that monitor. For example,
in order to register the following simple MyMon() monitor,
you need to call the method:
register("calls", MyMon());
from the init()method of
the internal() class.
# A simple monitor that
counts the number of method/procedure and built-in function calls
class MyMon(eventMask, pcalls, fcalls,
pcalls_rate, fcalls_rate)
# This method counts the number of method/procedure
calls
method handle_E_Pcall(code, value)
pcalls +:= 1
end
# This method counts the number of built-in
function calls
method handle_E_Fcall(code, value)
fcalls +:= 1
end
# This method do some calculations and analyze the
collected information
method analyze_info()
local total
total := pcalls + fcalls
pcalls_rate := pcalls/total * 100
fcalls_rate := fcalls/total * 100
end
# This method prints out some information
method write_info()
write(" # pcalls = ", pcalls,
" and its ratio is
:", pcalls_rate)
write(" # fcalls = ", fcalls,
" and its ratio is
:", fcalls_rate)
end
# Constructor
initially()
eventMask := cset(E_Pcall||E_Fcall)
pcalls := fcalls := 0
end
procedure main(args)
EvInit(args)
o := MyMon()
while EvGet(o.eventMask)
do{
case
&eventcode
of{
E_Pcall:{
o.handle_E_Pcall(&eventcode,
&eventvalue)
}
E_Fcall:{
o.handle_E_Fcall(&eventcode,
&eventvalue)
}
}
}
o.analyze_info()
o.write_info()
end
However, UDB's auto-registration has some restricted assumption about the used
monitor, especially its method names. In this auto registration, there is three
types of methods inside the monitor class that the auto-registration can
recognize:
1) Event handler methods: those methods start with the prefix "handle_"
followed by the event name. Each method supposed to handle one event. (i.e.
handle_E_Pcall())
2) Information analyzer methods: those methods start with the prefix "analyze_"
followed by any name. This type of method supposed to process and analyze
the collected information. (i.e.
analyze_info()). and finally
3) Information writer: those methods starts with the prefix "write_"
followed by any name. This type of methods supposed to write information
collected by the monitor. (i.e.
write_info()).
In fact, this suggested formatting is intended to simplify the registration
process. Users follow this format, all they have to do is to strip the monitor
from its main procedure, use the auto registration method, compile their
monitors and link them into the source code of UDB. At that point user are abele
to use their own monitors from within UDB and during the debugging session.
Different monitors are distinguished by their names. The user is liable to
enable/disable and use the monitor facilities on the fly during the debugging
session.
10.3.2 Complex monitors
Complex monitors are those monitors that do not follow the conventional
formatting. In such monitors, there is no restriction on how to
write the monitor itself, and there is on restriction on the naming of the used methods.
In order to register this type of monitors, the user has to call the method
register() with some extra parameters, which
used to
register three types of method: 1) event handler, 2) information analyzer, and
3) information or result writer. The method prototype is as follows:
register( "name", object, [handlers],
[analyzers], [writers],mask);
where
1) [handlers]: is a list of the string names of the methods used to handle
events in the external monitor,
2) [analyzers]: is a list of the string names of
the methods used to analyze the collected information in the external monitor,
3) [writers] : is a list of the string names of
the methods used to write or print information from the external monitor, and
4) mask : is the set of events that
the monitor is looking for
For example: the following monitor example, named
FailedLoop, does not follow the
suggested formatting. It has one handler method named "process_Events()"
, one analyzer method named "process_Info(j)",
and two writer methods "print_header()"
and "print_Info( num )". If you
noticed, the methods does not follow the suggested naming. In order to
register such a monitor, you need to call the method register() with the
following parameters:
register("calls", MyMon(),["process_Events"],["process_Info"],["print_header()","print_Info"],mask);
This way of registration provides an extra layer over the simple registration.
It performs the simple auto registration and adds to it those specified methods.
This type of registration intended to add an extra level of freedom to the user to write his/her stand-alone monitor in
the way that he wants and to register it, as an internal, with the least
possible modification. Here is a real monitor that finds failed loops and a
demonstration on how to register it as internal one.
$include "evdefs.icn"
link syntname
# check failed loop, loops that executes zero times
# otherwise it will show up how many times that loop executed
class FailedLoop : Listener(
eventMask, #
Monitored events for this detection
procCallCount, # Table counts the number of
times each procedure is called
loopFailCount, # Table counts the number of
time a loop has been iterated
FailedLoopInfo # a list with the failed loop
information
)
# collect loopes information
method process_Events()
static loopStart :=
"while
every repeat until"
static loopEnd :=
"endwhile
endevery endrepeat enduntil"
static fname, line, pname, k
local loop
if &eventcode
== E_Pcall then{
pname := image(&eventvalue)
pname := pname[find(" ", pname)+1: 0]
procCallCount[pname] +:= 1
}
else if &eventcode
== E_Syntax then{
loop := syntname(&eventvalue)
if
find(loop, loopStart) then{
line :=
keyword("line", Monitored)
fname :=
keyword("file", Monitored)
pname :=
image(proc(Monitored,0))
pname := pname[find(" ", pname)+1:
0]
k := fname||"("||line||"){"||pname||"}"||loop
loopFailCount[k] +:= 1
}
}
else if &eventcode
== E_Line then{
loop := syntname(keyword("syntax",Monitored))
if
find(loop, loopStart) then{
line :=
&eventvalue
fname :=
keyword("file", Monitored)
pname :=
image(proc(Monitored,0))
pname := pname[find("
", pname)+1: 0]
k := fname||"("||line||"){"||pname||"}"||loop
loopFailCount[k] +:=1
}
}
end
# updates the failed loop info,
# it receives the param j which is the most elapsed j loops
method process_Info(j)
local i:=1, loop, fname, pname,
line, p
if \loopFailCount & *loopFailCount
> 0 then{
if /j
then j := *loopFailCount + 1
else j >=:= *loopFailCount
+ 1
FailedLoopInfo := sort(loopFailCount,2)[1:j]
# 1-"Seq Num" |# 2-"File Name" | #
3-"Line #" |
# 4-"Proc Name" | # 5-"#call" |
# 6-"Loop Name" |
# 7-"Avg(Iter/Call)" | # 8-"Total Iterations" |
while i <= j
& \FailedLoopInfo do{
loop :=
get(FailedLoopInfo[i])
fname := loop[1 : p :=
find("(", loop)]
line := loop[p+1 : find(")",
loop)]
pname := loop[find("{",loop)+1
: p := find("}",loop)]
loop := loop[p+1 : 0]
push(FailedLoopInfo[i],
FailedLoopInfo[i][1]/procCallCount[pname],
loop,
procCallCount[pname],
pname,
line,
fname,
i
)
i +:= 1
}
}
end
# This method prints out the header of the
result
method print_header()
write("\n-- Loop's info [The Least
Iterated (",num,") Loops] --")
write(left("Num",3),
" | ",
left("File
Name",10), " : ",
left("Line
#",6),
" : ",
left("proc
Name",10), " : ",
left("#Calls",6),
" : ",
left("Loop
Name",10), " : ",
left("Avg(Iter/Call)",14),"
: ",
left("Total
Iteration",15)
)
end
# Prints out the total elapsed time of a loop in ms
# It is to be used in a console based application such as UDB
method print_Info( num )
local i:=1
update_Info(num)
/num := *FailedLoopInfo
while i <= num & \FailedLoopInfo
do{
write(left(FailedLoopInfo[i][1],3),
" | ",
left(FailedLoopInfo[i][2],10)," : ",
left(FailedLoopInfo[i][3],6), " : ",
left(FailedLoopInfo[i][4],10)," : ",
left(FailedLoopInfo[i][5],6), " : ",
left(FailedLoopInfo[i][6],10)," : ",
left(FailedLoopInfo[i][7],14)," : ",
left(FailedLoopInfo[i][8],15)
)
i +:= 1
}
end
# Initialize the class attributes
initially(name, state)
self.Listener.initially(name,
state)
eventMask := cset(E_Pcall
|| E_Syntax ||
E_Line)
procCallCount := table(0)
loopFailCount := table(-2)
FailedLoopInfo := []
end
# StandAlone is defined when this tool is used as a
stand-alone monitor.
# Otherwise, this tool can be statically linked into the main utop/udb
# source code
$ifdef StandAlone
link evinit
procedure main(arg)
local obj
EvInit(arg) |
stop(" **** can not initialize Monitor !!!")
obj := FailedLoop()
while
EvGet(obj.eventMask) do
obj.handle_Events()
obj.write_Info()
end
$endif
11. Future Work
A future version of
UDB will be running with every Unicon program silently as an observer, if there
is something interesting such as a runtime error, it will take the lead putting
the user in no time in the debugging process. This will save the user the hassle
of regenerating the cause of the bug once a gain inside the debugger. Hence,
having UDB as an observer in the back of every program does not cost
the execution time of the program any thing noticeable.
12. References