# BEGIN LICENSE BLOCK
# Version: CMPL 1.1
#
# The contents of this file are subject to the Cisco-style Mozilla Public
# License Version 1.1 (the "License"); you may not use this file except
# in compliance with the License.  You may obtain a copy of the License
# at www.eclipse-clp.org/license.
# 
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
# the License for the specific language governing rights and limitations
# under the License. 
# 
# The Original Code is  The ECLiPSe Constraint Logic Programming System. 
# The Initial Developer of the Original Code is  Cisco Systems, Inc. 
# Portions created by the Initial Developer are
# Copyright (C) 2006 Cisco Systems, Inc.  All Rights Reserved.
# 
# Contributor(s): 
# 
# END LICENSE BLOCK

General
-------
The debugger presents a _port_model_ to the user.
On the abstract machine level, we have _debugger_notifications_.
The basic debugger code maps the notifications and their parameters into ports.

The _debugger_notifications_ happen at call and exit points.  These
points are chosen because they correspond to synchronous machine
states, ie states where a prolog goal can be inserted.  To get a more
accurate trace (and to support mixing of debug/nondebug code), we also
have redo notifications.  This is relatively simple, since we almost
have call states (only problem inline chp).  This would also catch the
last failure out of traced code.

In this redesign of the debugger, particular care has been taken to
allow arbitrary mixing of code with and without debug instructions,
or swiching tracing on and off for subgoal.

Notifications:
    call/wake
    exit
    redo
    delay


Tracing deterministic execution
-------------------------------
Apart from triggering the notifications, the abstract machine also
maintains an ancestor (goal) stack in debugging mode. Implementation-wise,
this is just a normal data structure pushed onto the global stack.
It is composed of frames of the following form:

tf(
	Invoc,
	Goal,
	Depth,
	Choicepnt,
	ParentFrame,
	ProcedureId,	% includes module and procedure flags
        Priority,
        FilePath,
	Line,
	From,
	To,
        Module
 )

A TD register points to the top of this stack. When a frame is popped
on exit, the register gets trailed if necessary.

Debug events enter debug handler in Prolog, passing this ancestor stack
and possibly some additional information.


Tracing backtracking
--------------------
On failure the ancestor stack is popped (since it is a logical data
structure).  We save the invocation number, some flags (spied,
untraceable, ...) and the goal functor to an external array that
survives failure.  The arguments are lost.  Depth is implicit and
doesn't need to be saved.  Failures are traced together with NEXT and
REDO in the redo-notifier.

There are no special abstract machine instructions for tracing failures.
This would not be sufficient anyway because we can fail out of/into
traced/nontraced parts of the execution. The engine's retry/trust/throw
instructions therefore _always_ check whether something has to be traced.

The NEXT port presents a problem regarding mixing debugged and
nondebugged code:  The port is a procedure-internal port.  Its
notifications are within the procedure's code, while the corresponding
call and exit notifications are in the caller's code.  We could just
display a next port for the (visible) ancestor, or possibly a port
with no goal information (just location information like clause or
line number), but that must be restricted to debug-compiled
retry/trust instructions, otherwise we get all of them traced.  This
is now solved by only tracing NEXT if it is within debug-compiled code.

Inline disjunctions are traced as a NEXT port of the predicate that
contains them.


Tracing exit_block/leave
------------------------
Identical to failure tracing.
(Not yet done: leaving through several emulator invocations).


Mixing traced and untraced code
-------------------------------
The following deals with:

    compilation mode (pragma([no]debug))
    procedure flag settings (skipped)

We don't talk here about debugging commands (e.g. skip command),
they don't affect the notifications.

The most flexible approach would be the complete layered one:

    trace(Goal) switches tracing on for Goal and its subgoals

    notrace(Goal) switches tracing off for Goal if it was on.
	We could have a stronger version of notrace which prevents any
	re-enabling within (eg for the debug handler itself).
	Similar to skipped/fastskip.
	Probably useless.

    call(Goal) traces the call of Goal if there is a
    	traced ancestor which is not notrace/1. This is used
	to reenable tracing within findall and the like.

	If thats undesirable, a traced parent can be made skipped
	(partial solution, because untraced skipped parents don't
	disable the tracing...).

	Waking a suspension that has an invocation number
	(re)enables tracing like call/1. However it should not
	be possible to suppress this tracing by skipping the parent.

    code compiled with no debug instructions:
    	waking of (traced) suspensions and metacalls are shown.

notrace(Goal): this should be the same mechanism as used to stop the
debugger code tracing itself. A DONT_TRACE bit is set in the top frame.
It stops call events from being raised, ie no new frames should be
created. However, popping of existing frames due to failures etc must
still be traced.

Mechanism:
    	Trace frames have flags, only the top one is important:

	    TF_SKIPPED	
		If the predicate it belongs to was set to skipped.
	    	Don't trace anything except resumes.

	    TF_INTRACER	
	    	We are in the tracer code.
	    	Don't trace anything.

	    TF_TRACEROFF
		Tracer has been switched off (n command).
	    	Don't trace anything and dont ever reset this bit.

	An empty debugger stack is interpreted as having all bits set.

Metacalls:
    always shown, except when the current top frame has DONT_ENABLE set.

Resume:
    always shown if it has an invocation number.

trace/1:
    Enables debugging for the subgoal. If it occurs in a nested situation,
    it is treated like waking, ie. the goal is traced if the parent is
    not set to skipped.


Whether the debugger does anything is controlled:
    - by the top frame's flags (local in subgoal, logical)
    - by the port mask (global, non-logical)

Switching tracing off completely is done by setting the port filter to 0.


Mixing application and tracing code in the same execution
----------------------------------------------------------
The debugger's code must not trace itself...
Some state must be saved/restored across the notification handlers.



Selective trace modes
---------------------

SKIP:
goto =< skiplevel

JUMP level:
goto =< or >= jumplevel

LEAP:
spy flag set in frame. Could do C-level filtering and raise events
only if spied frame was pushed or popped.

JUMP invoc:
if invoc < nextinvoc, only check at C level, otherwise naive.

VAR SPY:
Best done by waking a goal that invokes the tracer.

ZAP to_port:
Prefiltering by port mask.

#define OfInterest(flags, invoc, depth) \
	( (procflags) & TRACEMODE \
	&& JMINLEVEL <= (depth) && (depth) <= JMAXLEVEL \
	&& ( JINVOC == 0  ||  JINVOC == (invoc) ) )


		JINVOC	JMINLEV	JMAXLEV		SPIED	TRACBL	SKIPPED

nodebug		0	0	inf		0	0	0
creep		0	0	inf		0	1	0
leap		0	0	inf		1	?	0
skip		0	0	depth		0	1	0
jump invoc	invoc	0	inf		0	1	0
jump level	0	depth	depth		0	1	0
   or			0	depth
   or			depth	inf



Emulator mechanism for call/exit notification
---------------------------------------------
(could do with some simplification, but we leave that until after
simplification of the abstract machine ...)


Debug_call (instruction for new compiler -- support source tracing)
        sets DBG_PRI,DBG_PORT,DBG_INVOC,DBG_PATH,DBG_LINE,DBG_FROM,DBG_TO,
	and raises event

Call-instruction
	sets up call and goes to event handler

handle_events
	save state in environment:
	    without debug event:
	    	- call arguments
	    	- PP of called predicate start
		- continuation after events is restore_code
	    with debug event:
	    	- additionally DBG_PRI,DBG_PORT,DBG_INVOC,
		  DBG_PATH,DBG_LINE,DBG_FROM,DBG_TO
		- continuation after events is restore_code_debug
		clear the debug event condition
	handles events other than debug

restore_code_debug
	read some state (the debug info) and call debugger
	continuation after debugger is restore_code

restore_code
	restore call state from environment
	insert trap for exit tracing

The method of inserting a 'trap' for exit notification is not the fastest
but the most robust one (wrt to getting out of sync with call/exits).
The trap consists of pushing a return address that points to code that
generates the exit notification, and then returns to the original point.


* Metacall tracing
If tracing needed, the metacall directly creates the save-environment with
areguments and DBG_XXX fields, then goes to handle non-debug events,
then restore_code_debug.

* Wake tracing
Like metacall tracing


Tracing DELAY
------------------
--- Regular delay:
We raise a DEBUG_DELAY_EVENT in make_suspension/3,4 and make the handler
the trace routine.  The event is raised after the suspension has been made
and assigned it an invocation number.

An alternative would be to make make_suspension regular,
so we can simply call the tracer afterwards.

-- Delay inside external:
Delays inside externals are traced by the external returning DEBUG_SUSP_EVENT
instead of PSUCCEED.  The handler retrieves a list of new suspensions
(using LD and DBG_DELAY_INVOC), and traces them.



Emulator mechanism for redo notification
----------------------------------------

retry/trust instruction
	save state in environment
		arguments
		det-flag
	call debugger

restore_code
	restore call/retry state


Call/Exit tracking using FIRST/LAST flags (not used currently)
--------------------------------------------------------------

This is a method that does not require explicit exit notifications.
Assume: first and last call in a clause are marked F and L respectively.
The L flag must be stored in the trace frame.
When encountering a non-first call, generate exit ports for all
stack frames that have the L flag set.

This is fragile as it relies on F/L flags being always correct and
never missing anywhere. Difficulties with hidden predicates for example.


Tracing of simple (emulator) builtins
--------------------------------------------------------------
Example:
    debug_call_simple        arity/2 1 string 1 7 13 -1 2 
    bi_arity                 a(1) a(1) 4 
    debug_exit_simple        0 ref(L0) 

The debug_xxx_simple instructions push/manipulate the top trace frame
directly, then raise an event as if raised by the builtin itself:
DEBUG_BIPCALL_EVENT
DEBUG_BIPEXIT_EVENT
The handlers do not receive any arguments, they access the trace stack
directly.


Tracing of simple (emulator) builtins
--------------------------------------------------------------

Only compiled call tracing implemented so far. Compiled sequence:

	Debug_esc proc CALL_PORT	-> raise DEBUG_BIPCALL_EVENT
	Escape proc
	Debug_esc _proc EXIT_PORT	-> raise DEBUG_BIPEXIT_EVENT

Simple tests are often executed with a shallow choicepoint, i.e. failure
is caught by Restore_bp instruction, which does not contain the checks
for REDO/FAIL tracing. Therefore, we generate an explicit FAIL port
instruction before (or after) the Restore_bp:

	Debug_esc _proc FAIL_PORT	-> raise DEBUG_BIPFAIL_EVENT
	Restore_bp

The Debug_esc instructions raise an exception, i.e. save all state and
enter an event handler, same mechanism as for event within builtin.
The handler pushes/pops the trace frame and traces the port. The exception
handling makes the handler look atomic, like the builtin would have been.

DELAY is not done yet. Maybe can be detected and inserted by the exit-handler.

Breakpoints
-----------

Breakpoints is set by setting a flag in the abstract machine instruction 
(debug_call) associated with the call that the breakpoint is associated
with. This flag is located as a bit in the Port word of the instruction.
In addition, the breakpoint mechanism assumes that the source information
for the call follows the Port word in the following order:

Port, FilePath, Line, From, To

as it is in the debug_call instruction. This must be updated if the
debug_call instruction is modified [or if breakpoint is allowed with other
instructions].  

The abstract machine code includes a BreakTable section (if the code
contains debug_call instructions with the Port word) for each predicate.
This table is generated by the assembler, with one word per Port word,
giving the offset to the Port word, and terminated by a 0.

When the predicate is loaded into memory, the breaktable word in the header
is initialised tp point to the break-table:

Header   Instr           BrkTable
|-----|-----------------|-------| 
   |____________________^


A breakpoint is set by setting the break flag of the Port word in the
debug_call instruction for the goal

The breakpoint information for a predicate is returned by get_flag/3.
The information gives the source file and line number of each breakport in
the predicate. This information is obtained by using the break-table to go
to each Port word, and then from there obtaining the file and line
information from  the abtract machine instruction argument. The port word
can also be examined to see if a break-point for a particular call is set
or not.

To be useful for a GUI, breakpoints are set from a file and line
number. All the predicates defined in that file are then examined to obtain
a matching port for the breakpoint.

Dropped features
-----------------

macros
	better done with user command hook + prolog code



TTY interface
---------------------

Navigation movements
- - - - - - - - - - -
<n><cr>			move to nth argument
0<cr>			move up to toplevel
right arrow (C)		move right
left arrow  (D)		move left
up arrow    (A)		move up
down arrow  (B)		move down

Ug<invoc>		use goal
U(<invoc>		use goal
Uw<n>			use watched term
Ur<[module:]name>	use global reference
Uv<[module:]name>	use global variable

Continue execution
- - - - - - - - - - -
c     creep
<cr>   creep
j,J   jump to
r,R   retry ?
l,L   leap
s,S   skip
v   var skip
<N>v  var skip to watched term N
z,Z   zap to port

a     abort
n     nodebug
b     break

Print stuff
- - - - - - - - - - -
d     delayed goals
g,G   ancestor(s)
h,?   help
u     woken goals
.     find defn
^     delayed var

Watch data
- - - - - - - - - - -
W	display the watched set
<n>w	inspect nth watched term
w+	add displayed term to watched set
<n>w-	remove displayed term <n> from watched set
wa	display always
wd	display on demand

No longer supported
- - - - - - - - - - -
f     fail	?
F     fast skip
x,X   back up	?

N     switch off
P     prt warnings

Low-level debugging
- - - - - - - - - - -
!     systrace
|     debug trace
*     inspect

Option setting
- - - - - - - - - - -
m     prt module
o     output mode
p     port prop
t     trace mode
+     spy this
-     nospy this
<     set print depth

Other
- - - - - - - - - - -
M     macro	?
T     macro table
@     command
H     handler inv



OPIUM-style interface
---------------------


