Debugging
There are two major ways to debug functions (and methods) at runtime: Tracing and single-stepping.Tracing means letting functions of interest print their name and arguments when they are entered, and their name again and the return value when they are exited.
For demonstration, let's define the unavoidable factorial function:
(de fact (N) (if (=0 N) 1 (* N (fact (- N 1))) ) )With trace we can put it in trace mode:
: (trace 'fact) -> factCalling 'fact' now will display its execution trace.
: (fact 3) fact : 3 fact : 2 fact : 1 fact : 0 fact = 1 fact = 1 fact = 2 fact = 6 -> 6As can be seen here, each level of function call will indent by an additional space. Upon function entry, the name is separated from the arguments with a colon (':'), and upon function exit with an equals sign ('=') from the return value.
'trace' works by modifying the function body, so generally only for functions defined as lists (lambda expressions, see Evaluation. Tracing a C-function is possible, however, when it is a function that evaluates all its arguments.
So let's trace the functions =0 and *:
: (trace '=0) -> =0 : (trace '*) -> *If we call 'fact' again, we see the additional output:
: (fact 3) fact : 3 =0 : 3 =0 = NIL fact : 2 =0 : 2 =0 = NIL fact : 1 =0 : 1 =0 = NIL fact : 0 =0 : 0 =0 = 0 fact = 1 * : 1 1 * = 1 fact = 1 * : 2 1 * = 2 fact = 2 * : 3 2 * = 6 fact = 6 -> 6To reset a function to its untraced state, call untrace
: (untrace 'fact) -> fact : (untrace '=0) -> =0 : (untrace '*) -> *or simply
: (mapc untrace '(fact =0 *)) -> *Single-stepping means to execute a function step by step, giving the programmer an opportunity to look more closely at what is happening. The function debug inserts a breakpoint into each top-level expression of a function. When the function is called, it stops at each breakpoint, displays the expression it is about to execute next (this expression is also stored into the global variable ^)
and enters a read-eval-loop. The programmer can then
- inspect the current environment by typing variable names or calling functions
- execute '(d)' to recursively debug the next expression
- execute '(e)' to evaluate the next expression, to see what will happen without actually advancing on
- type ENTER (: enter an empty line) to leave the read-eval loop and continue with the next expression
To try it out, let's look at the stamp system function.
: (pp 'stamp) (de stamp (Dat Tim) (and (=T Dat) (setq Dat (date T))) (default Dat (date) Tim (time T)) (pack (dat$ Dat "-") " " (tim$ Tim T)) ) -> stamp
: (debug 'stamp) # Debug it -> T : (stamp) # Call it again (and (=T Dat) (setq Dat (date T))) # stopped at first expression ! # ENTER (default Dat (date) Tim (time T)) # second expression ! # ENTER (pack (dat$ Dat "-") " " (tim$ ... # third expression ! Tim # inspect 'Tim' variable -> 41908 ! (time Tim) # convert it -> (11 38 28) ! # ENTER -> "2004-10-29 11:38:28" # done, as there are only 3 expressionsNow we execute it again, but this time we want to look at what's happening inside the second expression.
: (stamp) # Call it again (and (=T Dat) (setq Dat (date T))) ! # ENTER (default Dat (date) Tim (time T)) ! # ENTER (pack (dat$ Dat "-") " " (tim$ ... # here we want to look closer ! (d) # debug this expression -> T ! # ENTER (dat$ Dat "-") # stopped at first subexpression ! (e) # evaluate it -> "2004-10-29" ! # ENTER (tim$ Tim T) # stopped at second subexpression ! (e) # evaluate it -> "11:40:44" ! # ENTER -> "2004-10-29 11:40:44" # doneThe breakpoints still remain in the function body. We can see them when we pretty-print it:
: (pp 'stamp) (de stamp (Dat Tim) (! and (=T Dat) (setq Dat (date T))) (! default Dat (date) Tim (time T)) (! pack (! dat$ Dat "-") " " (! tim$ Tim T) ) ) -> stampTo reset the function to its normal state, call
: (unbug 'stamp)Often, you will not want to single-step a whole function. Just use 'edit' (see above) to insert a single breakpoint (the exclamation mark followed by a space) as CAR of an expression, and run your program. Execution will then stop there as described above; you can inspect the environment and continue execution with ENTER when you are done.
https://picolisp.com/wiki/?tutdbg
01nov10 | abu |