{Begin SubSec Breaking Functions and Debugging} {Title Breaking Functions and Debugging} {Text {Tag BreakingFunctions} {note The break package was written by W. Teitelman.} {index *PRIMARY* debugging} Debugging a collection of LISP functions involves isolating problems within particular functions and/or determining when and where incorrect data are being generated and transmitted. In the Interlisp system, there are three facilities which allow the user to (temporarily) modify selected function definitions so that he can follow the flow of control in his programs, and obtain this debugging information. All three redefine functions in terms of a system function, {fn BREAK1}{index BREAK1 FN} (see {PageRef Fn BREAK1}). {fn BREAK}{index BREAK FN} modifies the definition of a function {arg FN}, so that whenever {arg FN} is called and a break condition (defined by the user) is satisfied, a function break occurs. The user can then interrogate the state of the machine, perform any computation, and continue or return from the call. {fn TRACE}{index TRACE FN} modifies a definition of a function {arg FN} so that whenever {arg FN} is called, its arguments (or some other values specified by the user) are printed. When the value of {arg FN} is computed it is printed also. ({fn TRACE} is a special case of {fn BREAK}).{index BREAK FN} {index BREAKIN FN}{fn BREAKIN} allows the user to insert a breakpoint {it inside} an expression defining a function. When the breakpoint is reached and if a break condition (defined by the user) is satisfied, a temporary halt occurs and the user can again investigate the state of the computation. The following two examples illustrate these facilities. In the first example, the user traces the function {lisp FACTORIAL}. {fn TRACE} redefines {lisp FACTORIAL} so that it print its arguments and value, and then goes on with the computation. When an error occurs on the fifth recursion, a full interactive break occurs. The situation is then the same as though the user had originally performed {lisp BREAK(FACTORIAL)} instead of {lisp TRACE(FACTORIAL)}, and the user can evaluate various Interlisp forms and direct the course of the computation. In this case, the user examines the variable {lisp N}, and instructs {fn BREAK1} to return {lisp 1} as the value of this cell to {lisp FACTORIAL}. The rest of the tracing proceeds without incident. The user would then presumably edit {lisp FACTORIAL} to change {lisp L} to {lisp 1}. {lispcode _PP FACTORIAL (FACTORIAL [LAMBDA (N) (COND ((ZEROP N L) (T (ITIMES N (FACTORIAL (SUB1 N]) FACTORIAL _TRACE(FACTORIAL) (FACTORIAL) _FACTORIAL(4) FACTORIAL: N = 4 FACTORIAL: N = 3 FACTORIAL: N = 2 FACTORIAL: N = 1 FACTORIAL: N = 0 U.B.A. L (FACTORIAL BROKEN) :N 0 :RETURN 1 FACTORIAL = 1 FACTORIAL = 1 FACTORIAL = 2 FACTORIAL = 6 FACTORIAL = 24 24 _ } In the second example, the user has constructed a non-recursive definition of {lisp FACTORIAL}. He uses {index BREAKIN FN}{fn BREAKIN} to insert a call to {index BREAK1 FN}{fn BREAK1} just after the {fn PROG} label {lisp LOOP}. This break is to occur only on the last two iterations, when {lisp N} is less than {lisp 2}. When the break occurs, the user tries to look at the value of {lisp N}, but mistakenly types {lisp NN}. The break is maintained, however, and no damage is done. After examining {lisp N} and {lisp M} the user allows the computation to continue by typing {lisp OK}. A second break occurs after the next iteration, this time with {lisp N}={lisp 0}. When this break is released, the function {lisp FACTORIAL} returns its value of {lisp 120}. {lispcode _PP FACTORIAL (FACTORIAL [LAMBDA (N) (PROG ((M 1)) LOOP (COND ((ZEROP N) (RETURN M))) (SETQ M (ITIMES M N)) (SETQ N (SUB1 N)) (GO LOOP]) FACTORIAL _BREAKIN(FACTORIAL (AFTER LOOP) (ILESSP N 2] SEARCHING... FACTORIAL _FACTORIAL(5) ((FACTORIAL) BROKEN) :NN U.B.A. NN (FACTORIAL BROKEN AFTER LOOP) :N 1 :M 120 :OK (FACTORIAL) ((FACTORIAL) BROKEN) :N 0 :OK (FACTORIAL) 120 _ } Note: {fn BREAK} and {fn TRACE} can also be used on CLISP words{index breaking CLISP expressions} which appear as {fn CAR} of form, e.g. {lisp FETCH}, {lisp REPLACE}, {lisp IF}, {lisp FOR}, {lisp DO}, etc., even though these are not implemented as functions. For conditional breaking, the user can refer to the entire expression via the variable {index Var EXP}{var EXP}, e.g. {lisp BREAK ((FOR (MEMB 'UNTIL EXP)))}. {note this variable EXP should also be mentioned in the CLISP chapter} {FnDef {FnName BREAK0} {FnArgs FN WHEN COMS {anonarg} {anonarg}} {Text Sets up a break on the function {arg FN}; returns {arg FN}. If {arg FN} is not defined, returns {lisp ({arg FN} NOT DEFINED)}. {fn BREAK0} redefines {arg FN} as a call to {index BREAK1 FN}{fn BREAK1} ({PageRef Fn BREAK1}), with an equivalent definition of {arg FN} as {index BRKEXP Var}{var BRKEXP}, and {arg WHEN}, {arg FN}, {arg COMS} as {index BRKWHEN Var}{arg BRKWHEN}, {index BRKFN Var}{arg BRKFN}, {index BRKCOMS Var}{arg BRKCOMS}. Puts a {index GENSYM FN}{fn GENSYM} defined with the original definition of {arg FN} on the property list of {arg FN} under the property {index *PRIMARY* BROKEN Prop}{prop BROKEN} . Puts {lisp (BREAK0 {arg WHEN} {arg COMS})} on the property list of {arg FN} under the property {index BRKINFO Prop}{prop BRKINFO} (for use in conjunction with {index REBREAK FN}{fn REBREAK}). Adds {arg FN} to the front of the list {index BROKENFNS Var}{var BROKENFNS}. If {arg FN} is non-atomic and of the form {lisp ({arg FN1} IN {arg FN2})}, {fn BREAK0} breaks every call to {arg FN1} from within {arg FN2}. This is useful for breaking on a function that is called from many places, but where one is only interested in the call from a specific function, e.g., {lisp (RPLACA IN FOO)}, {lisp (PRINT IN FIE)}, etc. It is similar to {index BREAKIN FN}{fn BREAKIN} described below, but can be performed even when {arg FN2} is compiled or blockcompiled, whereas {index BREAKIN FN}{fn BREAKIN} only works on interpreted functions. If {arg FN1} is not found in {arg FN2}, {index BREAK0 FN}{fn BREAK0} returns the value {lisp ({arg FN1} NOT FOUND IN {arg FN2})}. {indexX {Name IN} {Type (arg to BREAK0)} {Text {lisp ({arg FN1} IN {arg FN2})}} } {indexX {Name NOT FOUND IN} {Type (value of BREAK0)} {Text {lisp ({arg FN1} NOT FOUND IN {arg FN2})}} } {fn BREAK0} breaks one function {it inside} another by first calling a function which changes the name of {arg FN1} wherever it appears inside of {arg FN2} to that of a new function, {lisp {arg FN1}-IN-{arg FN2}}, which is initially given the same function definition as {arg FN1}. Then {fn BREAK0} proceeds to break on {lisp {arg FN1}-IN-{arg FN2}} exactly as described above. In addition to breaking {lisp {arg FN1}-IN-{arg FN2}} and adding {lisp {arg FN1}-IN-{arg FN2}} to the list {index BROKENFNS Var}{var BROKENFNS}, {fn BREAK0} adds {arg FN1} to the property value for the property {index NAMESCHANGED Prop}{prop NAMESCHANGED} on the property list of {arg FN2} and puts {lisp ({arg FN2} . {arg FN1})} on the property list of {lisp {arg FN1}-IN-{arg FN2}} under the property {index *PRIMARY* ALIAS Prop}{prop ALIAS}. This will enable {index UNBREAK FN}{fn UNBREAK} to recognize what changes have been made and restore the function {arg FN2} to its original state. {note I assume that the values are ADDED to the NAMESCHANGED property, as compared to SETTING the value of that property. right?? on the other hand, the ALIAS prop of fn1-in-fn2 is SET. right??} If {arg FN} is nonatomic and not of the above form, {fn BREAK0} is called for each member of {arg FN} using the same values for {arg WHEN}, {arg COMS}, and {arg FILE}. This distributivity permits the user to specify complicated break conditions on several functions. For example, {lispcode (BREAK0 '(FOO1 ((PRINT PRIN1) IN (FOO2 FOO3))) '(NEQ X T) '(EVAL ?= (Y Z) OK) )} will break on {lisp FOO1}, {lisp PRINT-IN-FOO2}, {lisp PRINT-IN-FOO3}, {lisp PRIN1-IN-FOO2} and {lisp PRIN1-IN-FOO3}. {Note how about a simpler example} If {arg FN} is non-atomic, the value of {index BREAK0 FN}{fn BREAK0} is a list of the functions broken. }} {FnDef {FnName BREAK} {FnArgs X} {Type NOSPREAD NLAMBDA} {Text Nlambda nospread function. For each atomic argument, it performs {lisp (BREAK0 {arg ATOM} T)}. For each list, it performs {lisp (APPLY 'BREAK0 {arg LIST})}. For example, {lisp (BREAK FOO1 (FOO2 (GREATERP N 5) (EVAL)))} is equivalent to {lisp (BREAK0 'FOO1 T)} and {lisp (BREAK0 'FOO2 '(GREATERP N 5) '(EVAL))}. }} {FnDef {FnName TRACE} {FnArgs X} {Type NOSPREAD NLAMBDA} {Text Nlambda nospread function. For each atomic argument, it performs {lisp (BREAK0 {arg ATOM} T '(TRACE ?= NIL GO))}{foot The flag {lisp TRACE} is checked for in {index BREAK1 FN}{fn BREAK1} and causes the message "{lisp {arg FUNCTION} :}" to be printed instead of {lisp ({arg FUNCTION} BROKEN)}. }{comment endfootnote} For each list argument, {fn CAR} is the function to be traced, and {fn CDR} the forms the user wishes to see, i.e., {fn TRACE} performs: {lisp (BREAK0 (CAR {arg LIST}) T (LIST 'TRACE '?= (CDR {arg LIST}) 'GO))} For example, {lisp (TRACE FOO1 (FOO2 Y))} will cause both {lisp FOO1} and {lisp FOO2} to be traced. All the arguments of {lisp FOO1} will be printed; only the value of {lisp Y} will be printed for {lisp FOO2}. In the special case that the user wants to see {it only} the value, he can perform {lisp (TRACE ({arg FUNCTION}))}. This sets up a break with commands {lisp (TRACE ?= (NIL) GO)}. {note how is indenting of recursive traced functions done??} }} Note: the user can always call {index BREAK0 FN}{fn BREAK0} himself to obtain combination of options of {index BREAK1 FN}{fn BREAK1} not directly available with {index BREAK FN}{fn BREAK} and {index TRACE FN}{fn TRACE}. These two functions merely provide convenient ways of calling {fn BREAK0}, and will serve for most uses. {Begin Note} Date: 8 FEB 1982 2053-PST From: KAPLAN.PA Subject: Calls/breakin glitches If you say (BREAK (FOO IN FIE)), and FOO happens to be called from a compiler generated subfunction of FIE, then no break takes place. On the other hand, if you say CALLS(FIE), the compiler generated subfunctions don't show up as being called, which makes it difficult to discover what subfunctions there are in which FOO may be broken explicitly. Seems to me that the guy that does the breaking from compiled code ought to scan subfunctions. This might also be the way it works on the 10 (I didn't check), but it is more of a problem in D because ERSETQ's generate subfunctions. Anybody understand this? {End Note} {index *BEGIN* BREAKIN FN} {FnDef {FnName BREAKIN} {FnArgs FN WHERE WHEN COMS} {Type NLAMBDA} {Text {fn BREAKIN} is an nlambda function. {arg WHEN} and {arg COMS} are similar to {arg WHEN} and {arg COMS} for {index BREAK0 FN}{fn BREAK0}, except that if {arg WHEN} is {lisp NIL}, {lisp T} is used. {arg WHERE} specifies where in the definition of {arg FN} the call to {index BREAK1 FN}{fn BREAK1} is to be inserted (see below). If {arg FN} is a compiled function, {fn BREAKIN} returns {lisp ({arg FN} UNBREAKABLE)} as its value. {indexX {Name UNBREAKABLE} {Type (value of BREAKIN)} {Text {lisp ({arg FN} UNBREAKABLE)}} } If {arg FN} is interpreted, {fn BREAKIN} types {lisp SEARCHING...}{index SEARCHING... (Printed by BREAKIN)} while it calls the editor. If the location specified by {arg WHERE} is not found, {fn BREAKIN} types {lisp (NOT FOUND)} and exits. If it is found, {fn BREAKIN} puts {lisp T} under the property {index *PRIMARY* BROKEN-IN Prop}{prop BROKEN-IN} and {lisp ({arg WHERE} {arg WHEN} {arg COMS})} under the the property {index BRKINFO Prop}{prop BRKINFO} on the property list of {arg FN}, and adds {arg FN} to the front of the list {index BROKENFNS Var}{var BROKENFNS}. {indexX {Name NOT FOUND} {Type (printed by BREAKIN)} {Text {lisp (NOT FOUND)}} } Multiple break points, can be inserted with a single call to {fn BREAKIN} by using a list of the form {lisp ((BEFORE {ellipsis}) {ellipsis} (AROUND {ellipsis}))} for {arg WHERE}. It is also possible to call {index BREAK FN}{fn BREAK} or {index TRACE FN}{fn TRACE} on a function which has been modified by {fn BREAKIN}, and conversely to {fn BREAKIN} a function which has been redefined by a call to {fn BREAK} or {fn TRACE}. }} {fn BREAKIN} enables the user to insert a break, i.e., a call to {index BREAK1 FN}{fn BREAK1}, at a specified location in an interpreted function. For example, if {lisp FOO} calls {lisp FIE}, inserting a break in {lisp FOO} before the call to {lisp FIE} is similar to breaking {lisp FIE}. However, {fn BREAKIN} can be used to insert breaks before or after {fn PROG} labels, particular {fn SETQ} expressions, or even the evaluation of a variable. This is because {fn BREAKIN} operates by calling the editor and actually inserting a call to {fn BREAK1} at a specified point {it inside} of the function. The user specifies where the break is to be inserted by a sequence of editor commands. These commands are preceded by {index *PRIMARY* BEFORE (as argument to BREAKIN)}{lisp BEFORE}, {index *PRIMARY* AFTER (as argument to BREAKIN)}{lisp AFTER}, or {index *PRIMARY* AROUND (as argument to BREAKIN)}{lisp AROUND}, which {fn BREAKIN} uses to determine what to do once the editor has found the specified point, i.e., put the call to {index BREAK1 FN}{fn BREAK1} {lisp BEFORE} that point, {lisp AFTER} that point, or {lisp AROUND} that point. For example, {lisp (BEFORE COND)} will insert a break before the first occurrence of {fn COND}, {lisp (AFTER COND 2 1)} will insert a break after the predicate in the first {fn COND} clause, {lisp (AFTER BF (SETQ X &))} after the {it last} place {lisp X} is set. Note that {lisp (BEFORE TTY:)} or {lisp (AFTER TTY:)}{index TTY: EditCom} permit the user to type in commands to the editor, locate the correct point, and verify it for himself using the {editcom P} command if he desires, and exit from the editor with {editcom OK}.{foot A {index STOP EditCom}{editcom STOP} command typed to {index TTY: EditCom}{lisp TTY:} produces the same effect as an unsuccessful edit command in the original specification, e.g., {lisp (BEFORE CONDD)}. In both cases, the editor aborts, and {fn BREAKIN} types {lisp (NOT FOUND)}.{index (NOT FOUND) (Printed by BREAKIN)} }{comment endfootnote} {fn BREAKIN} then inserts the break {lisp BEFORE}, {lisp AFTER}, or {lisp AROUND} that point. For {fn BREAKIN} {index BEFORE (as argument to BREAKIN)}{lisp BEFORE} or {index AFTER (as argument to BREAKIN)}{lisp AFTER}, the break expression is {lisp NIL}, since the value of the break is irrelevant. For breakin {index AROUND (as argument to BREAKIN)}{lisp AROUND}, the break expression will be the indicated form. In this case, the user can use the {index EVAL BreakCom}{breakcom EVAL} command to evaluate that form, and examine its value, before allowing the computation to proceed. For example, if the user inserted a break after a {fn COND} predicate, e.g., {lisp (AFTER (EQUAL X Y))}, he would be powerless to alter the flow of computation if the predicate were not true, since the break would not be reached. However, by breaking {lisp (AROUND (EQUAL X Y))}, he can evaluate the break expression, i.e., {lisp (EQUAL X Y)}, look at its value, and return something else if he wished. The message typed for a {fn BREAKIN} break, is {lisp (({arg FN}) BROKEN}), where {arg FN} is the name of the function inside of which the break was inserted. Any error, or typing {index control-E}control-E, will cause the full identifying message to be printed, e.g., {lisp (FOO BROKEN AFTER COND 2 1)}. A special check is made to avoid inserting a break inside of an expression headed by any member of the list {index NOBREAKS Var}{var NOBREAKS}, initialized to {lisp (GO QUOTE *)}, since this break would never be activated. For example, if {lisp (GO L)} appears before the label {lisp L}, {fn BREAKIN} {lisp (AFTER L)} will not insert the break inside of the {fn GO} expression, but skip this occurrence of {lisp L} and go on to the next {lisp L}, in this case the label {lisp L}. Similarly, for {lisp BEFORE} or {lisp AFTER} breaks, {fn BREAKIN} checks to make sure that the break is being inserted at a "safe" place. For example, if the user requests a break {lisp (AFTER X)} in {lisp (PROG {ellipsis} (SETQ X &) {ellipsis})}, the break will actually be inserted {lisp AFTER (SETQ X &)}, and a message printed to this effect, e.g., {lisp BREAK INSERTED AFTER (SETQ X &)}.{index BREAK INSERTED AFTER (Printed by BREAKIN)} {index *END* BREAKIN FN} {FnDef {FnName UNBREAK} {FnArgs X} {Type NOSPREAD NLAMBDA} {Text Nlambda nospread function. It takes an indefinite number of functions modified by {index BREAK FN}{fn BREAK}, {index TRACE FN}{fn TRACE}, or {index BREAKIN FN}{fn BREAKIN} and restores them to their original state by calling {fn UNBREAK0}. Returns list of values of {fn UNBREAK0}. {lisp (UNBREAK)} will unbreak all functions on {index BROKENFNS Var}{var BROKENFNS}, in reverse order. It first sets {index BRKINFOLST Var}{var BRKINFOLST} to {lisp NIL}. {index UNBREAK FN}{lisp (UNBREAK T)} unbreaks just the first function on {var BROKENFNS}, i.e., the most recently broken function. }} {FnDef {FnName UNBREAK0} {FnArgs FN {anonarg}} {Text Restores {arg FN} to its original state. If {arg FN} was not broken, value is {lisp (NOT BROKEN)} and no changes are made. If {arg FN} was modified by {fn BREAKIN}, {index UNBREAKIN FN}{fn UNBREAKIN} is called to edit it back to its original state. If {arg FN} was created from {lisp ({arg FN1} IN {arg FN2})}, (i.e., if it has a property {index ALIAS Prop}{prop ALIAS}), the function in which {arg FN} appears is restored to its original state. All dummy functions that were created by the break are eliminated. Adds property value of {index BRKINFO Prop}{prop BRKINFO} to (front of) {index BRKINFOLST Var}{var BRKINFOLST}. {indexX {Name NOT BROKEN} {Type (value of UNBREAK0)} {Text {lisp (NOT BROKEN)}} } Note: {lisp (UNBREAK0 '({arg FN1} IN {arg FN2}))} is allowed: {index UNBREAK0 FN}{fn UNBREAK0} will operate on {lisp ({arg FN1}-IN-{arg FN2})} instead. }} {FnDef {FnName UNBREAKIN} {FnArgs FN} {Text Performs the appropriate editing operations to eliminate all changes made by {index BREAKIN FN}{fn BREAKIN}. {arg FN} may be either the name or definition of a function. Value is {arg FN}. {fn UNBREAKIN} is automatically called by {index UNBREAK FN}{fn UNBREAK} if {arg FN} has property {index BROKEN-IN Prop}{prop BROKEN-IN} with value {lisp T} on its property list. }} {FnDef {FnName REBREAK} {FnArgs X} {Type NOSPREAD NLAMBDA} {Text Nlambda nospread function for rebreaking functions that were previously broken without having to respecify the break information. For each function on {arg X}, {index REBREAK FN}{fn REBREAK} searches {var BRKINFOLST} for break(s) and performs the corresponding operation. Value is a list of values corresponding to calls to {index BREAK0 FN}{fn BREAK0} or {index BREAKIN FN}{fn BREAKIN}. If no information is found for a particular function, returns {lisp ({arg FN} - NO BREAK INFORMATION SAVED)}. {indexX {Name NO BREAK INFORMATION SAVED} {Type (value of REBREAK)} {Text {lisp ({arg FN} - NO BREAK INFORMATION SAVED)}} } {lisp (REBREAK)} rebreaks everything on {index BRKINFOLST Var}{var BRKINFOLST}, so {lisp (REBREAK)} is the inverse of {lisp (UNBREAK)}. {lisp (REBREAK T)} rebreaks just the first break on {var BRKINFOLST}, i.e., the function most recently unbroken. }} {note should CHANGENAME be moved?? where??} {FnDef {FnName CHANGENAME} {FnArgs FN FROM TO} {Text Changes all occurrences of {arg FROM} to {arg TO} in {arg FN}. {arg FN} may be compiled or blockcompiled.{index editing compiled code} Value is {arg FN} if {arg FROM} was found, otherwise {lisp NIL}. Does not perform any modifications of property lists. Note that {arg FROM} and {arg TO} do not have to be functions, e.g., they can be names of variables, or any other literals. }} {FnDef {FnName VIRGINFN} {FnArgs FN FLG} {Text The function that knows how to restore functions to their original state regardless of any amount of breaks, breakins, advising, compiling and saving exprs, etc. It is used by {fn PRETTYPRINT}, {fn DEFINE}, and the compiler. If {arg FLG}={lisp NIL}, as for {fn PRETTYPRINT}, it does not modify the definition of {arg FN} in the process of producing a "clean" version of the definition; it works on a copy. If {arg FLG}={lisp T}, as for the compiler and {fn DEFINE}, it physically restores the function to its original state, and prints the changes it is making, e.g., {lisp FOO UNBROKEN}{index UNBROKEN (Printed by System)}, {lisp FOO UNADVISED}{index UNADVISED (Printed by System)}, {lisp FOO NAMES RESTORED},{index NAMES RESTORED (Printed by System)} etc. Returns the virgin function definition. }} }{End SubSec Breaking Functions and Debugging}