1% BEGIN LICENSE BLOCK
2% Version: CMPL 1.1
3%
4% The contents of this file are subject to the Cisco-style Mozilla Public
5% License Version 1.1 (the "License"); you may not use this file except
6% in compliance with the License.  You may obtain a copy of the License
7% at www.eclipse-clp.org/license.
8% 
9% Software distributed under the License is distributed on an "AS IS"
10% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
11% the License for the specific language governing rights and limitations
12% under the License. 
13% 
14% The Original Code is  The ECLiPSe Constraint Logic Programming System. 
15% The Initial Developer of the Original Code is  Cisco Systems, Inc. 
16% Portions created by the Initial Developer are
17% Copyright (C) 2006 Cisco Systems, Inc.  All Rights Reserved.
18% 
19% Contributor(s): 
20% 
21% END LICENSE BLOCK
22\section{Debugger}
23
24
25\subsection{General}
26
27\index{debugger}
28\index{tracer}
29\index{port model}
30The debugger presents a {\bf port model} to the user.
31On the abstract machine level, we have {\bf debugger notifications}.
32The mapping of debugger notifications into ports is done
33in Prolog (in file tracer.pl).
34
35The debugger notifications\index{notifications}  happen at {\bf call} and {\bf exit} points.  These
36points are chosen because they correspond to synchronous machine
37\index{synchronous state}
38states, ie states where a prolog goal can be inserted.  To get a more
39accurate trace (and to support mixing of debug/nondebug code), we also
40have {\bf redo} notifications at retry/trust instructions.
41This is relatively simple, since we almost
42have call states (except at the choicepoints on inline disjunctions).
43The retry/trust instructions unconditionally check whether the debugger
44has to be notified (even when the instructions are not inside debug-compiled
45code). This is necessary in order to catch failures that leave the traced part
46of the code.
47
48For the tracing of coroutining\index{coroutining}, we also have a {\bf delay} notification
49(in the make_suspension/3 built-in) and a {\bf wake} notification
50(in the call_suspension/1 primitive).
51\index{make_suspension/3}
52
53In this redesign of the debugger, particular care has been taken to
54allow arbitrary mixing of code with and without debug instructions,
55or switching tracing on and off for a subgoal.
56
57
58\subsection{Tracing deterministic execution}
59
60\index{ancestor stack}
61Apart from triggering the notifications, the abstract machine also
62maintains an ancestor (goal) stack, when in debugging mode. 
63Implementation-wise, this is just a normal data structure pushed onto
64the global stack.  It is composed of frames of the following form:
65\begin{verbatim}
66tf(
67        Invoc,
68        Goal,
69        Depth,
70        Choicepnt,
71        ParentFrame,
72        ProcedureId     % includes module and procedure flags
73 )
74\end{verbatim}
75
76A TD\index{TD}  register points to the top of this stack. When a frame is popped
77on exit, the register gets trailed if necessary.
78
79Debug notification events cause invocation of a corresponding handler
80in Prolog, passing this ancestor stack and possibly some additional
81information.
82
83
84\subsection{Tracing backtracking}
85
86On failure, the ancestor stack is popped (since it is a logical data
87structure).  We save the invocation number, some flags (spied,
88untraceable, ...) and the goal functor to an external array that
89survives failure.  The arguments are lost.  Depth is implicit and
90doesn't need to be saved.  Failures are traced together with NEXT and
91REDO in the redo-notifier.
92
93There are no special abstract machine instructions for tracing failures.
94This would not be sufficient anyway because we can fail out of/into
95traced/nontraced parts of the execution. The engine's retry/trust/throw
96instructions therefore {\em always} check whether something has to be traced.
97
98The NEXT port presents a problem regarding mixing debugged and
99nondebugged code:  The port is a procedure-internal port.  Its
100notifications are within the procedure's code, while the corresponding
101call and exit notifications are in the caller's code.  We could just
102display a next port for the (visible) ancestor, or possibly a port
103with no goal information (just location information like clause or
104line number), but that must be restricted to debug-compiled
105retry/trust instructions, otherwise we get all of them traced.  This
106is now solved by only tracing NEXT if it is within debug-compiled code.
107
108Inline disjunctions are traced as a NEXT port of the predicate that
109contains them.
110
111Tracing exit_block/leave ports is done like failure tracing.
112
113
114\subsection{Mixing traced and untraced code}
115
116The following deals with:
117\begin{enumerate}
118\item compilation mode (pragma([no]debug))
119\item procedure flag settings (skipped)
120\end{enumerate}
121We don't talk here about debugging commands (e.g. skip command),
122they don't affect the notifications.
123
124% The most flexible approach would be the complete layered one:
125% 
126%     trace(Goal) switches tracing on for Goal and its subgoals
127% 
128%     notrace(Goal) switches tracing off for Goal if it was on.
129%         We could have a stronger version of notrace which prevents any
130%         re-enabling within (eg for the debug handler itself).
131%         Similar to skipped/fastskip.
132%         Probably useless.
133% 
134%     call(Goal) traces the call of Goal if there is a
135%         traced ancestor which is not notrace/1. This is used
136%         to reenable tracing within findall and the like.
137% 
138%         If thats undesirable, a traced parent can be made skipped
139%         (partial solution, because untraced skipped parents don't
140%         disable the tracing...).
141% 
142%         Waking a suspension that has an invocation number
143%         (re)enables tracing like call/1. However it should not
144%         be possible to suppress this tracing by skipping the parent.
145% 
146%     code compiled with no debug instructions:
147%         waking of (traced) suspensions and metacalls are shown.
148% 
149% 
150% notrace(Goal): this should be the same mechanism as used to stop the
151% debugger code tracing itself. A DONT_TRACE bit is set in the top frame.
152% It stops call events from being raised, ie no new frames should be
153% created. However, popping of existing frames due to failures etc must
154% still be traced.
155
156Mechanism: Trace frames have flags, only the top one is important:
157\begin{description}
158\item[TF_SKIPPED] 
159                If the predicate it belongs to was set to skipped.
160                Don't trace anything except resumes.
161
162\item[TF_INTRACER] 
163                We are in the tracer code.
164                Don't trace anything.
165\end{description}
166An empty debugger stack is interpreted as having all bits set.
167\begin{description}
168
169\item[Metacalls:]
170    always shown, except when the current top frame has DONT_ENABLE set.
171
172\item[Resume:]
173    always shown if it has an invocation number.
174
175\item[trace/1:]
176    Enables debugging for the subgoal. If it occurs in a nested situation,
177    it is treated like waking, ie. the goal is traced if the parent is
178    not set to skipped.
179\end{description}
180
181
182Whether the debugger does anything is controlled:
183\begin{itemize}
184\item by the top debug frame's flags (locally for the subgoal)
185\item by the port mask (globally for the rest of the execution)
186\end{itemize}
187
188Switching tracing off completely is done by setting the port filter to 0.
189
190
191\subsection{Mixing application and tracing code in the same execution}
192
193The debugger's code must not trace itself. Therfore
194some state must be saved/restored across the notification handlers.
195
196
197
198\subsection{Tracing delay/wake}
199
200%Make make_suspension (DELAY) regular so we can call the tracer.
201%Or raise a suspend-event in make_suspension and make the handler
202We raise a suspend-event in make_suspension and make the handler
203the trace routine. The suspension must be a parameter.
204Its invocation number field can be set from Prolog in the tracer.
205
206
207\subsection{Pre-filtering}
208\index{port filtering}
209To implement the selective tracer modes (skip, jump, leap, etc)
210the tracer has a number of registers that support fast pre-filtering
211of tracer ports on a low level. These are
212\begin{itemize}
213\item tracemode flags (e.g.\ leap/skip)
214\item min_level, max_level for filtering on the nesting level
215\item min_invoc, max_invoc for filtering on the invocation number
216\item port_filter for filtering the port name
217\end{itemize}
218The pre-filtering can be done with a relatively cheap test:
219\begin{small}
220\begin{verbatim}
221#define OfInterest(flags, invoc, depth) \
222        ( (procflags) & TRACEMODE \
223        && JMINLEVEL <= (depth) && (depth) <= JMAXLEVEL \
224        && ( JINVOC == 0  ||  JINVOC == (invoc) ) )
225\end{verbatim}
226\end{small}
227and the different skip instructions can be implemented by configuring
228the pre-filter registers as follows:
229\begin{small}
230\begin{verbatim}
231                JINVOC  JMINLEV JMAXLEV         SPIED   TRACBL  SKIPPED
232
233nodebug         0       0       inf             0       0       0
234creep           0       0       inf             0       1       0
235leap            0       0       inf             1       ?       0
236skip            0       0       depth           0       1       0
237jump invoc      invoc   0       inf             0       1       0
238jump level      0       depth   depth           0       1       0
239   or                   0       depth
240   or                   depth   inf
241\end{verbatim}
242\end{small}
243
244
245
246\subsection{Emulator mechanism for call/exit notification}
247
248The notifications are currently handles by dynamically inserting 
249code sequences into the success continuation. Hopefully this
250can be simplified in release 6, after
251simplification of the abstract machine.
252\begin{small}
253\begin{verbatim}
254Debug_call instruction:
255        sets DBG_PRI,DBG_PORT,DBG_INVOC and raises event
256
257Call instruction:
258        sets up call and goes to event handler
259
260handle_events routine:
261        save state in environment:
262            without debug event:
263                - call arguments
264                - PP of called predicate start
265                - continuation after events is restore_code
266            with debug event:
267                - additionally DBG_PRI,DBG_PORT,DBG_INVOC
268                - continuation after events is restore_code_debug
269                clear the debug event condition
270        handles events other than debug
271
272restore_code_debug abstract code fragment:
273        read some state (the debug info) and call debugger
274        continuation after debugger is restore_code
275
276restore_code abstract code fragment:
277        restore call state
278        insert trap for exit tracing
279\end{verbatim}
280\end{small}
281
282
283\subsection{Emulator mechanism for redo notification}
284\begin{small}
285\begin{verbatim}
286retry/trust instruction:
287        save state in environment
288                arguments
289                det-flag
290        call debugger
291
292restore_code abstract code fragment:
293        restore call/retry state
294\end{verbatim}
295\end{small}
296
297
298%\subsection{Call/Exit tracking using FIRST/LAST flags (not used currently)}
299%
300%This is a method that does not require explicit exit notifications.
301%Assume: first and last call in a clause are marked F and L respectively.
302%The L flag must be stored in the trace frame.
303%When encountering a non-first call, generate exit ports for all
304%stack frames that have the L flag set.
305%
306%This is fragile as it relies on F/L flags being always correct and
307%never missing anywhere. Difficulties with hidden predicates for example.
308
309
310\subsection{Tracing of simple (emulator) builtins}
311
312Only compiled call tracing implemented so far. Compiled sequence:
313\begin{verbatim}
314	Debug_esc proc CALL_PORT	-> raise DEBUG_BIPCALL_EVENT
315	Escape proc
316	Debug_esc _proc EXIT_PORT	-> raise DEBUG_BIPEXIT_EVENT
317\end{verbatim}
318Simple tests are often executed with a shallow choicepoint, i.e. failure
319is caught by Restore_bp instruction, which does not contain the checks
320for REDO/FAIL tracing. Therefore, we generate an explicit FAIL port
321instruction before (or after) the Restore_bp:
322\begin{verbatim}
323	Debug_esc _proc FAIL_PORT	-> raise DEBUG_BIPFAIL_EVENT
324	Restore_bp
325\end{verbatim}
326The Debug_esc instructions raise an exception, i.e. save all state and
327enter an event handler, same mechanism as for event within builtin.
328The handler pushes/pops the trace frame and traces the port. The exception
329handling makes the handler look atomic, like the builtin would have been.
330
331DELAY is not done yet. Maybe can be detected and inserted by the exit-handler.
332
333
334\subsection{Tracing delay ports of suspensions created in externals}
335
336If an external predicate (one which is implemented in C) creates suspensions
337for which DELAY ports should be generated, the external should return
338DEBUG_SUSP_EVENT instead of PSUCCEED. This will lead to invocation of
339a handler which generates DELAY ports for all new suspensions created
340since the ancestor CALL port.
341
342
343