% 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
\section{Debugger}


\subsection{General}

\index{debugger}
\index{tracer}
\index{port model}
The debugger presents a {\bf port model} to the user.
On the abstract machine level, we have {\bf debugger notifications}.
The mapping of debugger notifications into ports is done
in Prolog (in file tracer.pl).

The debugger notifications\index{notifications}  happen at {\bf call} and {\bf exit} points.  These
points are chosen because they correspond to synchronous machine
\index{synchronous state}
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 {\bf redo} notifications at retry/trust instructions.
This is relatively simple, since we almost
have call states (except at the choicepoints on inline disjunctions).
The retry/trust instructions unconditionally check whether the debugger
has to be notified (even when the instructions are not inside debug-compiled
code). This is necessary in order to catch failures that leave the traced part
of the code.

For the tracing of coroutining\index{coroutining}, we also have a {\bf delay} notification
(in the make_suspension/3 built-in) and a {\bf wake} notification
(in the call_suspension/1 primitive).
\index{make_suspension/3}

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


\subsection{Tracing deterministic execution}

\index{ancestor stack}
Apart from triggering the notifications, the abstract machine also
maintains an ancestor (goal) stack, when 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:
\begin{verbatim}
tf(
        Invoc,
        Goal,
        Depth,
        Choicepnt,
        ParentFrame,
        ProcedureId     % includes module and procedure flags
 )
\end{verbatim}

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

Debug notification events cause invocation of a corresponding handler
in Prolog, passing this ancestor stack and possibly some additional
information.


\subsection{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 {\em 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 ports is done like failure tracing.


\subsection{Mixing traced and untraced code}

The following deals with:
\begin{enumerate}
\item compilation mode (pragma([no]debug))
\item procedure flag settings (skipped)
\end{enumerate}
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:
\begin{description}
\item[TF_SKIPPED] 
                If the predicate it belongs to was set to skipped.
                Don't trace anything except resumes.

\item[TF_INTRACER] 
                We are in the tracer code.
                Don't trace anything.
\end{description}
An empty debugger stack is interpreted as having all bits set.
\begin{description}

\item[Metacalls:]
    always shown, except when the current top frame has DONT_ENABLE set.

\item[Resume:]
    always shown if it has an invocation number.

\item[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.
\end{description}


Whether the debugger does anything is controlled:
\begin{itemize}
\item by the top debug frame's flags (locally for the subgoal)
\item by the port mask (globally for the rest of the execution)
\end{itemize}

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


\subsection{Mixing application and tracing code in the same execution}

The debugger's code must not trace itself. Therfore
some state must be saved/restored across the notification handlers.



\subsection{Tracing delay/wake}

%Make make_suspension (DELAY) regular so we can call the tracer.
%Or raise a suspend-event in make_suspension and make the handler
We raise a suspend-event in make_suspension and make the handler
the trace routine. The suspension must be a parameter.
Its invocation number field can be set from Prolog in the tracer.


\subsection{Pre-filtering}
\index{port filtering}
To implement the selective tracer modes (skip, jump, leap, etc)
the tracer has a number of registers that support fast pre-filtering
of tracer ports on a low level. These are
\begin{itemize}
\item tracemode flags (e.g.\ leap/skip)
\item min_level, max_level for filtering on the nesting level
\item min_invoc, max_invoc for filtering on the invocation number
\item port_filter for filtering the port name
\end{itemize}
The pre-filtering can be done with a relatively cheap test:
\begin{small}
\begin{verbatim}
#define OfInterest(flags, invoc, depth) \
        ( (procflags) & TRACEMODE \
        && JMINLEVEL <= (depth) && (depth) <= JMAXLEVEL \
        && ( JINVOC == 0  ||  JINVOC == (invoc) ) )
\end{verbatim}
\end{small}
and the different skip instructions can be implemented by configuring
the pre-filter registers as follows:
\begin{small}
\begin{verbatim}
                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
\end{verbatim}
\end{small}



\subsection{Emulator mechanism for call/exit notification}

The notifications are currently handles by dynamically inserting 
code sequences into the success continuation. Hopefully this
can be simplified in release 6, after
simplification of the abstract machine.
\begin{small}
\begin{verbatim}
Debug_call instruction:
        sets DBG_PRI,DBG_PORT,DBG_INVOC and raises event

Call instruction:
        sets up call and goes to event handler

handle_events routine:
        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
                - continuation after events is restore_code_debug
                clear the debug event condition
        handles events other than debug

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

restore_code abstract code fragment:
        restore call state
        insert trap for exit tracing
\end{verbatim}
\end{small}


\subsection{Emulator mechanism for redo notification}
\begin{small}
\begin{verbatim}
retry/trust instruction:
        save state in environment
                arguments
                det-flag
        call debugger

restore_code abstract code fragment:
        restore call/retry state
\end{verbatim}
\end{small}


%\subsection{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.


\subsection{Tracing of simple (emulator) builtins}

Only compiled call tracing implemented so far. Compiled sequence:
\begin{verbatim}
	Debug_esc proc CALL_PORT	-> raise DEBUG_BIPCALL_EVENT
	Escape proc
	Debug_esc _proc EXIT_PORT	-> raise DEBUG_BIPEXIT_EVENT
\end{verbatim}
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:
\begin{verbatim}
	Debug_esc _proc FAIL_PORT	-> raise DEBUG_BIPFAIL_EVENT
	Restore_bp
\end{verbatim}
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.


\subsection{Tracing delay ports of suspensions created in externals}

If an external predicate (one which is implemented in C) creates suspensions
for which DELAY ports should be generated, the external should return
DEBUG_SUSP_EVENT instead of PSUCCEED. This will lead to invocation of
a handler which generates DELAY ports for all new suspensions created
since the ancestor CALL port.


