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