========================================================
(C)1993 Institute for New Generation Computer Technology
(Read COPYRIGHT for detailed information.)
========================================================

About the KLIC Tracer
=====================
November 26, 1993
Takashi Chikayama (ICOT)

[1] Tracing Execution of Your Program

When you want to trace execution of a program, you have to make a
traced version of its executable.  It can be done by simply giving
"-t" option to the command "klic" when you compile your programs.

If you already have compiled the program without the trace option, it
will not recompile the program from scratch; it will only link the
object with the tracing version of the runtime library, which takes
much shorter time.

The current version of the KLIC tracer depends on "nm" to know the
addresses of program modules, predicate descriptors &c.  It was tested
on SunOS 4.1.X and SunOS 5.2 (Solaris 2.X), but we are not sure
whether it works on other systems.  Do not "strip" the executable, as
it will strip off the information required by the tracer.


[2] Trace Ports

Execution of KL1 programs proceeds as follows.

  1. The initial goal (main:main) is put into a pool of goals to be
     executed.
  2. One goal is taken from the goal pool (CALL).
  3. The goal is matched against the program clauses.
  4. If any of the clauses matches the goal, the goal is reduced into
     subgoals and they are put back to the goal pool (REDUCE).
  5. If no clause matches the goal, then the whole computation will be
     aborted (FAIL).
  6. If values of goal arguments or their substructures are not
     defined and thus it is not possible yet to decide whether some
     clauses will match the goal or not, the goal is put into another
     goal pool awaiting for required values (SUSPEND).
  7. If there still remains some goals in the goal pool, loop back to
     the item 2.

Execution of a goal can be traced on four of the above listed points,
numbered 2, 4, 5 and 6.  Such points of interest are called "trace
ports" and referenced as "CALL", "REDUCE", "FAIL" and "SUSPEND" ports,
respectively.

[Note] Those who are accustomed to the four-port trace model of Prolog
may wonder why two other ports of Prolog, "EXIT" and "REDO" are
missing.  The "REDO" port does not exist because KL1 programs do not
backtrack.  The "EXIT" port is not traced for two reasons.  First,
keeping track of all the goal-subgoal hierarchy is much more costly
for a concurrent language such as KL1 than for sequential languages
such as Prolog.  Execution of many different subtrees of the hierarchy
interleave each other because of the data-flow synchronization
feature.  The other reason is that, KL1 programs are often written as
a set of communicating processes each defined as a goal calling the
same predicate in a tail-recursive fashion.  Such processes (sometimes
called "perpetual processes") will almost never finish and detecting
their termination is not as meaningful as in Prolog.


[3] Trace Output

Below is our sample program for explanation here.

	:- module main.

	main :- nrev([1,2],X), builtin:print(X).

	nrev([], R) :- R = [].
	nrev([W|X], R) :- nrev(X, XR), append(XR, [W], R).

	append([], Y, Z) :- Z = Y.
	append([W|X], Y, WZ) :- WZ = [W|Z], append(X, Y, Z).

		[ Program ]

Listed below is output of a full trace of execution of the sample
program.

	   1 CALL:main:main? 
	   1 REDU:main:main :-
	   2   0:+nrev([1,2],_4)
	   3   1:+builtin:print(_4)? 
	   2 CALL:main:nrev([1,2],_4)? 
	   2 REDU:main:nrev([1,2],_4) :-
	   4   0:+nrev([2],_D)
	   5   1:+append(_D,[1],_4)? 
	   4 CALL:main:nrev([2],_D)? 
	   4 REDU:main:nrev([2],_D) :-
	   6   0:+nrev([],_18)
	   7   1:+append(_18,[2],_D)? 
	   6 CALL:main:nrev([],_18)? 
	   6 REDU:main:nrev([],[])? 
	   7 CALL:main:append([],[2],_D)? 
	   7 REDU:main:append([],[2],[2])? 
	   5 CALL:main:append([2],[1],_4)? 
	   5 REDU:main:append([2],[1],[2|_1F]) :-
	   8   0:+append([],[1],_1F)? 
	   8 CALL:main:append([],[1],_1F)? 
	   8 REDU:main:append([],[1],[1])? 
	   3 CALL:builtin:print([2,1])? 
	[2,1]
	   3 REDU:builtin:print([2,1])? 

As this program does not make any suspensions nor failures, all the
trace outputs here are either at the call or the reduce port (marked
as "REDU").

The first line of the above is the trace of the call port of the
initial goal main:main.

	   1 CALL:main:main? 

All the traced goals are given a unique identifier (an integer value)
to distinguish them among themselves.  The number "1" in the first
column here is the identifier of the initial goal.

The initial goal matches the first clause defined in the program and
thus reduced into subgoals as defined in the program clause.  This
reduction is traced as follows.

	   1 REDU:main:main :-
	   2   0:+nrev([1,2],_4)
	   3   1:+builtin:print(_4)? 

This shows that the original goal (main:main) with ID 1 is reduced
into two new goals, main:nrev([1,2],_4) and builtin:print(_4), with
IDs 2 and 3 respectively.

The numbers 0 and 1 following the IDs 2 and 3 of the two new goals are
sequential numbers for the subgoals generated by the reduction.  They
are used by some tracer commands to identify which subgoal to apply
the command to.  Unlike unique goal IDs that has global meaning, these
subgoal numbers are meaningful only at this specific port.

Next comes ":", which means the subgoal is an ordinary subgoal of the
parent goal.  There are other possibilities here.  The character "*"
means that the goal following it is also a subgoal, but is given a
priority different from the parent.  The priority is displayed in a
pseudo-pragma format.  The character "!"  means that the goal
following it is not actually a subgoal reduced from the parent goal,
but is a goal awaiting for some variable value which has just waken up
as this reduction gave some concrete value to the variable.

Then comes either "+" or "-".  "+" means that the subgoal will be
traced if you simply continue the execution, and "-" means it will
not.  This can be changed by giving some tracer commands described
below.  In the above example, all of the subgoals had "+" as all goals
are traced.

Then the module name, a colon character, and the predicate name of the
subgoal are displayed.  The module name for predicates defined in the
same module as the predicate of the parent goal is omitted with the
colon for brevity.  In the above example, the subgoal calling "nrev"
(that is "main") does not have its module name displayed, as it is the
same as the parent goal "main:main".

Finally comes the argument list in parentheses separated by commas.
The second argument of nrev and the only argument of print is "_4",
which corresponds to a variable corresponding to "X" in the source
program.  As variables are newly allocated for all incarnation of
predicate clauses, and as two or more variables can be unified
together, displaying their original names in the source program is not
meaningful.  They are given unique names such as "_4".

[Note] Actually, this number 4 somehow corresponds to the physical
memory address of the variable.  It will thus change completely after
garbage collections.  However, as garbage collections are not so
frequent, the address information is still quite useful for debugging.

The trace output stops after displaying all the subgoals and a
question mark.  Here, you can input one of the trace commands
described below.


[4] Execution Control Commands

The following commands are available for controlling program
execution.

  Continue: <carriage return> or 'c'
	Continues stepping execution.  Subgoal marked as '-' are not
	traced even in stepping mode.

  Leap: 'l'
	Continues execution without tracing until a spy point is
	encountered.  See [7] Spying Commands for details.

  Skip: 's'
	Continues execution of the traced goal and all subgoals thereof
	without tracing them at all.  Even spy points are neglected.

  Abort: 'a'
	Aborts whole execution of the program.

These commands do not take any arguments.


[5] Trace Control Commands

Tracing of each subgoals (displayed as '+' or '-') can be switched by
the following commands.

  Trace: '+' <subgoal number> ...
	Switches on the trace of the specified subgoal(s).
	Multiple subgoal numbers separated by spaces can be specified.
	If no subgoal numbers are given, all the subgoals become traced.

  No Trace: '-' <subgoal number> ...
	Switches off the trace of the specified subgoal(s).
	Multiple subgoal numbers separated by spaces can be specified.
	If no subgoal numbers are given, all the subgoals become untraced.

  Toggle Trace: <subgoal number> ...
	Toggles the trace switch of the specified subgoal(s).
	Multiple subgoal numbers separated by spaces can be specified.


[6] Default Trace Control Commands

By default, all the subgoals of a goal will have trace switch on ('+')
initially at the reduce port.  This default setting can be changed
predicate by predicate using commands described in this section, so
that predicates you are not interested in will not be traced by
default.

In what follows, command arguments <predicate> has one of the
following format.

    ModuleName:PredicateName/Arity
	Specifies explicitly and exactly one predicate.
	Eg. main:nrev/2
    ModuleName:PredicateName
	Specifies all the predicates with in a module with different
	arity.
    ModuleName:
	Specifies all the predicates defined in a module.  Note that a
	colon is required after the module name to distinguish it from
	a predicate name.
    PredicateName/Arity
	Specifies the predicate defined in the same module as the
	predicate of the currently traced goal with the given name and
	arity.
    PredicateName
	Specifies all the predicates defined in the same module as the
	predicate of the currently traced goal with the given name.

  No Trace Default: 'n' <Predicate> ...
	Set the default trace for the predicate(s) to be off.
	If no predicates are given as argument, the predicate of the
	traced goal is considered to be specified.

  Trace Default: 't' <Predicate> ...
	Set the default trace for the predicate(s) to be on.
	If no predicates are given as argument, the predicate of the
	traced goal is considered to be specified.


[7] Spying Commands

It is often the case that only some specific predicates are of
interest for debugging.  In such cases, ports for such predicates can
be specified as the "spy points".  You can let program run without
tracing until some spy point is encountered, using the leap ('l')
command described above.  Commands described in this section sets and
resets such spy points.

  Spy: 'S' <Predicate> ...
	Set the predicate(s) as spied.
	If not predicates are given as argument, the predicate of the
	traced goal is spied.

  No Spy: 'N' <Predicate> ...
	Reset the spy point on the predicate(s).
	If not predicates are given as argument, the predicate of the
	traced goal is spied.


[8] Port Control Commands

The four trace ports can selectively enabled and disabled.  Disabled
ports will not be traced at all.

In addition, for each port, you can specify whether to stop and wait
for command input.  Ports where execution stops and waits for commands
are said to be "leashed".  On ports enabled but not leashed, only the
trace output will be displayed and execution continues with the
default setting, as if the continue command (carriage return) were
input immediately.  For spied predicates, even unleashed ports will be
leashed.

Commands described in this section is for controlling such attributes
of ports.  They take port names as their arguments, specified as one
of the following ways.

	Port		Format
	-----------------------------------
	Call:		"c", "call"
	Reduce:		"r", "redu", "reduce"
	Suspend:	"s", "susp", "suspend"
	Fail:		"f", "fail"
	All ports:	"a", "all"

  Enable Port: 'E' <port> ...
	Enables the specified port(s).

  Disable Port: 'D' <port> ...
	Disables the specified port(s).

  Leash Port: 'L' <port> ...
	Leashes the specified port(s).

  Unleash Port: 'U' <port> ...
	Unleashes the specified port(s)


[9] Display Control Commands

Sometimes, full information of the traced goals is not desirable, as
too much information is only harmful for understanding the program
behavior.  Thus, commands in this section are provided for controlling
the amount of information displayed on trace ports.

  Set Print Depth: 'pd' <depth>
	Sets depth limit of displaying data structures to <depth>.
	With no argument, prints the current depth limit value.

  Set Print Length: 'pl' <length>
	Sets length limit of displaying data structures to <length>.
	With no argument, prints the current length limit value.

  Toggle Verbose Print: 'pv'
	Toggles verbose printing mode switch.
	In verbose printing mode, variables with goals awaiting for
	its value are displayed with the information of the goal.

Arguments of structures below depth limit are displayed in the
following way.

	f(a,b,c,d,e)	-->	f(..)
	[a,b,c,d,e]	-->	[..]

Argument list of structures longer than the length limit are displayed
in the following way.

	f(a,b,c,d,e)	-->	f(a,b,c,..)
	[a,b,c,d,e]	-->	[a,b,c,..]

The initial setting of depth and length limits are 3 and 7,
respectively.  Verbose print mode is initially switched off.


[10] Miscellaneous Commands

  Status Query: '='
	Displays tracer status information, such as follows.

		   port: Call Susp Redu Fail
		enabled:  +    +    +    +
		leashed:  +    +    +    +
		print terse; depth = 3; length = 7

  List Modules: 'lm'
	Lists all the modules of the currently executed program.

  List Predicates: 'lp'
	Lists all the predicates and their default trace status of the
	currently executed program.

  Queue: 'Q'
	Lists the contents of the ready queue.

  Help: '?' or 'h'
	Lists all the commands and their terse description available
	at the current port, in the following way.

	*** Commands available at REDUCE ports ***
    <cr>: continue;   c: continue;      s: skip;          l: leap;
    + <n>: trace;     - <n>: no trace;  <n>: toggle trace
    E: enable port;   D: disable port;  L: leash port;    U: unleash port;
    S <pred>: spy;    N <pred>: nospy;  t <pred>: trace;  n <pred>: notrace;
    pd: print depth;  pl: print length; pv: toggle verbose print;
    =: debug status   lm: list modules; lp: list predicates;
    a: abort execution


[11] Bugs

  - The current version depends on specific output formats of "nm" and
    thus may not be portable.

  - The current version of the tracer is not designed to trace
    programs running physically in parallel.

  - Some way to detect and report processes in deadlock should be
    provided.

  - Some way to inspect data structures appearing as goal arguments in
    detail and interactively should be provided.

  - Some way to keep specific goals or their arguments for later
    inspection should be provided.

  - Tools for performance debugging should be provided.

  - Better on-line documentation is needed.

  - This document is incomplete.

  - This list of bugs may be incomplete.

If you find some bugs or inconvenience with the tracer, please send
the information to the following address.

	klic-bugs@icot.or.jp
