{Begin SubSec Breaks} {Title Breaks} {Text {Tag BreakPackage} {index *PRIMARY* Break package} One of the most useful debugging facilities in Interlisp is the ability to put the system into a "break", stopping a computation at any point and allowing the user to interrogate the state of the world and affect the course of the computation. When a break occurs, a "break window"{index Break windows} (see {PageRef Term Break windows}) is brought up near the tty window of the process that broke. The break window appears to the user like a top-level executive window, except that the prompt character "{lisp :}"{index prompt character}{index *PRIMARY* : (Printed by System)} is used to indicate that the executive is ready to accept input, in the same way that "{lisp _}" is used at the top-level executive.{index _ (Printed by System)} However, a break saves the environment where the break occurred, so that the user may evaluate variables and expressions in the environment that was broken. In addition, the break program recognizes a number of useful "break commands", which provide an easy way to interrogate the state of the broken computation. Breaks may be entered in several different ways. Some interrupt characters ({PageRef Term Interrupt Characters}) automatically cause a break to be entered whenever they are typed. Function errors may also cause a break, depending on the depth of the computation (see {PageRef Tag WhenToBreak}). Finally, Interlisp provides facilities which make it easy to "break" suspect functions so that they always cause a break whenever they are entered, to allow examination and debugging (see {PageRef Fn BREAK}). Within a break the user has access to all of the power of Interlisp; he can do anything that he can do at the top-level executive. For example, the user can evaluate an expression, see that the value is incorrect, call the editor, change the function, and evaluate the expression again, all without leaving the break. The user can also type in commands to the programmer's assistant ({PageRef Term Programmer's Assistant}), e.g. to redo or undo previously executed events, including break commands. Similarly, the user can prettyprint functions, define new functions or redefine old ones, load a file, compile functions, time a computation, etc. In short, anything that he can do at the top level can be done while inside of the break. In addition the user can examine the stack (see {PageRef Tag STACK}), and even force a return back to some higher function via the function {fn RETFROM} or {fn RETEVAL}. It is important to emphasize that once a break occurs, the user is in complete control of the flow of the computation, and the computation will not proceed without specific instruction from him. If the user types in an expression whose evaluation causes an error, the break is maintained. Similarly if the user aborts a computation initiated from within the break (by typing {index control-E (Interrupt Character)}control-E), the break is maintained. Only if the user gives one of the commands that exits from the break, or evaluates a form which does a {fn RETFROM} or {fn RETEVAL} back out of {fn BREAK1}, will the computation continue. Also, {fn BREAK1} does not "turn off" control-D, so a {index control-D (Interrupt Character)}control-D will force an immediate return back to the top level. }{End SubSec Breaks} {Begin SubSec Break Windows} {Title Break Windows} {Text {index *PRIMARY* Break windows} When a break occurs, a break window is brought up near the tty window of the process that broke and the terminal stream switched to it. The title of the break window is changed to give the name of the broken function and the reason for the break. If a break occurs under a previous break, a new break window is created. Note: If a break is caused by a storage full error, the display break package will not try to open a new break window, since this would cause the error to occur repeatedly. {indexX {Name EVAL} {Type Break Window Command} {Text {lisp !EVAL}} } {index *PRIMARY* !EVAL (Break Window Command)} {index *PRIMARY* EVAL (Break Window Command)} {index *PRIMARY* EDIT (Break Window Command)} {index *PRIMARY* revert (Break Window Command)} {index *PRIMARY* ^ (Break Window Command)} {index *PRIMARY* OK (Break Window Command)} {index *PRIMARY* BT (Break Window Command)} {index *PRIMARY* BT! (Break Window Command)} {index *PRIMARY* ?= (Break Window Command)} While in a break window, the clicking middle button brings up a menu of break commands: {lisp !EVAL}, {lisp EVAL}, {lisp EDIT}, {lisp revert}, {lisp ^}, {lisp OK}, {lisp BT}, {lisp BT!}, and {lisp ?=}. Clicking on most of these commands is equivalent to typing the corresponding break command ({PageRef Term Break commands}). Clicking {lisp BT} and {lisp BT!}, however, is different from the typed-in backtrace break commands. The {lisp BT} and {lisp BT!} menu commands bring up a backtrace menu beside the break window showing the frames on the stack. {lisp BT} shows frames for which {fn REALFRAMEP} is {lisp T}; {lisp BT!} shows all frames. When one of the frames is selected from the backtrace menu, it is grayed and the function name and the variables bound in that frame (including local variables and {fn PROG} variables) are printed in the {index *PRIMARY* Backtrace frame window}"backtrace frame window." If the left button is used for the selection, only named variables are printed. If the middle button is used, all variables are printed (variables without names will appear as {lisp *var*{arg N}}). The "backtrace frame" window is an inspect window (see {PageRef Tag Inspector}). In this window, the left button can be used to select the name of the function, the names of the variables or the values of the variables. For example, below is a picture of a break window with a backtrace menu created by {lisp BT}. The {lisp OPENSTREAM} stack frame has been selected, so its variables are shown in an inspect window on top of the break window: {biglispcode 0""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""8poc{۷]v'?"۰g1l"w{vگ]vm;ocv o""h"b"bxDh"D!lh""D!lb""DQTb"xPA "Th"@2! >Th""@2!"Tb"@"Dbhh"b"0bCCÈ>8phR*B$I Dh"B$J Db"S*B#|!bhh"b"b@hP"@h"# @b"R L<@bAC"@hDH"@h"H"@b"bhh"b"b@>hPh"#b"R LPbACPhDH0h"H0b"bhh"b"b@hP"h"#"b"R LPbAC"PhDH"0h"H"0b"bhh"b"bhh"b"bhh"b"bhh"b"bhh"b"bhh"b"bhh"b"bhh"""?$Ob6(<@! &Hh|<@! &Hh"D} {Args EXPR} {Noparens} {Text Can be used in a break following either an unbound atom error, or an undefined function error. Replaces the expression containing the error with {arg EXPR} (not the value of {arg EXPR}), and continues the computation. {lisp ->} does not just change {index BRKEXP Var}{var BRKEXP}; it changes the function or expression containing the erroneous form. In other words, the user does not have to perform any additional editing. For example, {lispcode UNDEFINED CAR OF FORM (FOO1 BROKEN) :-> FOO} changes the {lisp FOO1} to {lisp FOO} and continues the computation. {arg EXPR} need not be atomic, e.g., {lispcode UNBOUND ATOM (FOO BROKEN) :-> (QUOTE FOO)} For undefined function breaks, the user can specify a function {it and} initial arguments, e.g., {lispcode UNDEFINED CAR OF FORM MEMBERX (MEMBERX BROKEN) :-> MEMBER X} Note that in the case of a undefined function error occurring immediately following a call to {fn APPLY} (e.g., {lisp (APPLY X Y)} where the value of {lisp X} is {lisp FOO} and {lisp FOO} is undefined), or a unbound atom error immediately following a call to {fn EVAL} (e.g., {lisp (EVAL X)}, where the value of {lisp X} is {lisp FOO} and {lisp FOO} is unbound), there {it is} no expression containing the offending atom. In this case, {breakcom ->} cannot operate, so {lisp ?} is printed and no action is taken. }} {Def {Type BreakCom} {Name EDIT} {Text Designed for use in conjunction with breaks caused by errors. Facilitates editing the expression causing the break: {lispcode NON-NUMERIC ARG NIL (IPLUS BROKEN) :EDIT IN FOO... (IPLUS X Z) EDIT *(3 Y) *OK FOO :} and the user can continue by typing {breakcom OK}, {breakcom EVAL}, etc. }} This command is very simple conceptually, but its implementation is complicated by all of the exceptional cases involving interactions with compiled functions, breaks on user functions, error breaks, breaks within breaks, et al. Therefore, we shall give the following simplified explanation which will account for 90% of the situations arising in actual usage. For those others, {breakcom EDIT} will print an appropriate failure message and return to the break. {breakcom EDIT} begins by searching up the stack beginning at {index LASTPOS Var}{var LASTPOS} (set by {index @ BreakCom}{breakcom @} command, initially position of the break) looking for a form, i.e., an internal call to {fn EVAL}. Then {index EDIT BreakCom}{breakcom EDIT} continues from that point looking for a call to an interpreted function, or to {fn EVAL}. It then calls the editor on either the {lisp EXPR} or the argument to {fn EVAL} in such a way as to look for an expression {fn EQ} to the form that it first found. It then prints the form, and permits interactive editing to begin. Note that the user can then type successive {lisp 0}'s to the editor to see the chain of superforms for this computation. If the user exits from the edit with an {breakcom OK}, the break expression{index break expression} is reset, if possible, so that the user can continue with the computation by simply typing {index OK BreakCom}{breakcom OK}. (Note that evaluating the new {index BRKEXP Var}{var BRKEXP} will involve reevaluating the form that causes the break, so that if {lisp (PUTD (QUOTE (FOO)) {arg BIG-COMPUTATION})} were handled by {breakcom EDIT}, {arg BIG-COMPUTATION} would be reevaluated.) However, in some situations, the break expression cannot be reset.{index NOTE: BRKEXP NOT CHANGED. (Printed by Break)} For example, if a compiled function {lisp FOO} incorrectly called {fn PUTD} and caused the error {lisp ARG NOT ATOM} followed by a break on {fn PUTD}, {breakcom EDIT} might be able to find the form headed by {lisp FOO}, and also find {it that} form in some higher interpreted function. But after the user corrected the problem in the {lisp FOO}-form, if any, he would still not have informed {breakcom EDIT} what to do about the immediate problem, i.e., the incorrect call to {fn PUTD}. However, if {lisp FOO} were {it interpreted}, {breakcom EDIT} would find the {fn PUTD} form itself, so that when the user corrected that form, {breakcom EDIT} could use the new corrected form to reset the break expression. {Def {Type BreakCom} {Name IN?} {Text Similar to {breakcom EDIT}, but just prints parent form, and superform, but does not call the editor, e.g., {lispcode ATTEMPT TO RPLAC NIL T (RPLACD BROKEN) :IN? FOO: (RPLACD X Z)} }} Although {index EDIT BreakCom}{breakcom EDIT} and {breakcom IN?}{index IN? BreakCom} were designed for error breaks, they can also be useful for user breaks. For example, if upon reaching a break on his function {lisp FOO}, the user determines that there is a problem in the {it call} to {lisp FOO}, he can edit the calling form and reset the break expression with one operation by using {lisp EDIT}. {index *END* break commands} }{End SubSec Break Commands} {Begin SubSec Controlling When to Break} {Title Controlling When to Break} {Text {Tag WhenToBreak} When an error occurs, the system has to decide whether to reset and unwind the stack, or go into a break. In the middle of a complex computation, it is usually helpful to go into a break, so that the user may examine the state of the computation. However, if the computation has only proceeded a little when the error occurs, such as when the user mistypes a function name, the user would normally just terminate a break, and it would be more convenient for the system to simply cause an error and unwind the stack in this situatuation. The decision over whether or not to induce a break depends on the depth of computation, and the amount of time invested in the computation. The actual algorithm is described in detail below; suffice it to say that the parameters affecting this decision have been adjusted empirically so that trivial type-in errors do not cause breaks, but deep errors do. {FnDef {Name BREAKCHECK} {Args ERRORPOS ERXN} {Text {fn BREAKCHECK} is called by the error routine to decide whether or not to induce a break when a error occurs. {arg ERRORPOS} is the stack position at which the error occurred; {arg ERXN} is the error number. Returns {lisp T} if a break should occur; {lisp NIL} otherwise. {fn BREAKCHECK} returns {lisp T} (and a break occurs) if the "computation depth" is greater than or equal to {index *PRIMARY* HELPDEPTH Var}{var HELPDEPTH}. {var HELPDEPTH} is initially set to 7, arrived at empirically by taking into account the overhead due to {fn LISPX} or {fn BREAK}. If the depth of the computation is less than {var HELPDEPTH}, {fn BREAKCHECK} next calculates the length of time spent in the computation. If this time is greater than {index *PRIMARY* HELPTIME Var}{var HELPTIME} milliseconds, initially set to 1000, then {fn BREAKCHECK} returns {lisp T} (and a break occurs), otherwise {lisp NIL}. }} {fn BREAKCHECK} determines the "computation depth" by searching back up the stack looking for an {fn ERRORSET}{index ERRORSET FN} frame ({fn ERRORSET}s indicate how far back unwinding is to take place when an error occurs, see {PageRef Fn ERRORSET}). At the same time, it counts the number of internal calls to {fn EVAL}. As soon as the number of calls to {fn EVAL} exceeds {var HELPDEPTH}, {fn BREAKCHECK} immediately stops searching for an {fn ERRORSET} and returns {lisp T}. Otherwise, {fn BREAKCHECK} continues searching until either an {fn ERRORSET} is found or the top of the stack is reached. (Note: If the second argument to {fn ERRORSET} is {lisp INTERNAL}, the {fn ERRORSET} is ignored by {fn BREAKCHECK} during this search.) {fn BREAKCHECK} then counts the number of function calls between the error and the last {fn ERRORSET}, or the top of the stack. The number of function calls plus the number of calls to {fn EVAL} (already counted) is used as the "computation depth". {fn BREAKCHECK} determines the computation time by subtracting the value of the variable {index *PRIMARY* HELPCLOCK Var}{var HELPCLOCK} from the value of {lisp (CLOCK 2)}, the number of milliseconds of compute time (see {PageRef Fn CLOCK}). {var HELPCLOCK} is rebound to the current value of {lisp (CLOCK 2)} for each computation typed in to {fn LISPX} or to a break. The time criterion for breaking can be suppressed by setting {var HELPTIME} to {lisp NIL} (or a very big number), or by setting {var HELPCLOCK} to {lisp NIL}. Note that setting {var HELPCLOCK} to {lisp NIL} will not have any effect beyond the current computation, because {var HELPCLOCK} is rebound for each computation typed in to {fn LISPX} and {fn BREAK}. The user can suppress all error breaks by setting the top level binding of the variable {index *PRIMARY* HELPFLAG Var}{var HELPFLAG} to {lisp NIL} using {fn SETTOPVAL} ({var HELPFLAG} is bound as a local variable in {fn LISPX}, and reset to the global value of {var HELPFLAG} on every {fn LISPX} line, so just {fn SETQ}ing it will not work.) If {var HELPFLAG}={lisp T} (the initial value), the decision whether to cause an error or break is decided based on the computation time and the computation depth, as described above. Finally, if {var HELPFLAG}={lisp BREAK!}, a break will always occur following an error. }{End SubSec Controlling When to Break} {Begin SubSec Break Window Variables} {Title Break Window Variables} {Text The appearance and use of break windows is controlled by the following variables: {FnDef {Name WBREAK} {Args ONFLG} {Text If {arg ONFLG} is non-{lisp NIL}, break windows and trace windows are enabled. If {arg ONFLG} is {lisp NIL}, break windows are disabled (break windows do not appear, but the executive prompt is changed to "{lisp :}" to indicate that the system is in a break). {fn WBREAK} returns {lisp T} if break windows are currently enabled; {lisp NIL} otherwise. }} {VarDef {Name MaxBkMenuWidth}} {VarDef {Name MaxBkMenuHeight} {Text The variables {var MaxBkMenuWidth} (default 125) and {var MaxBkMenuHeight} (default 300) control the maximum size of the backtrace menu. If this menu is too small to contain all of the frames in the backtrace, it is made scrollable in both vertical and horizontal directions. }} {VarDef {Name AUTOBACKTRACEFLG} {Text This variable controls when and what kind of backtrace menu is automatically brought up. The value of {var AUTOBACKTRACEFLG} can be one of the following: {Begin Labeledlist AUTOBACKTRACEFLG} {Label {lisp NIL}} {Text The backtrace menu is not automatically brought up (the default).} {Label {lisp T}} {Text On error breaks the {lisp BT} menu is brought up.} {Label {lisp BT!}} {Text On error breaks the {lisp BT!} menu is brought up.} {Label {lisp ALWAYS}} {Text The {lisp BT} menu is brought up on both error breaks and user breaks (calls to functions broken by {fn BREAK}).} {Label {lisp ALWAYS!}} {Text On both error breaks and user breaks the {lisp BT!} menu is brought up.} {End Labeledlist AUTOBACKTRACEFLG} }} {VarDef {Name BACKTRACEFONT} {Text The backtrace menu is printed in the font {var BACKTRACEFONT}. }} {VarDef {Name CLOSEBREAKWINDOWFLG} {Text The system normally closes break windows after the break is exited. If {var CLOSEBREAKWINDOWFLG} is {lisp NIL}, break windows will not be closed on exit. Note that in this case, the user must close all break windows. }} {VarDef {Name BREAKREGIONSPEC} {Text Break windows are positioned near the tty window of the broken process, as determined by the variable {var BREAKREGIONSPEC}. The value of this variable is a region ({PageRef (Record) REGION}) whose {lisp LEFT} and {lisp BOTTOM} fields are an offset from the {lisp LEFT} and {lisp BOTTOM} of the tty window. The {lisp WIDTH} and {lisp HEIGHT} fields of {var BREAKREGIONSPEC} determine the size of the break window. }} {VarDef {Name TRACEWINDOW} {Text The trace window, {var TRACEWINDOW}, is used for tracing functions. It is brought up when the first tracing occurs and stays up until the user closes it. {var TRACEWINDOW} can be set to a particular window to cause the tracing formation to print there. }} {VarDef {Name TRACEREGION} {Text The trace window is first created in the region {lisp TRACEREGION}. }} }{End SubSec Break Window Variables} ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) * BMOBJ.GETFN2    !  " A=z