Previous: The Scheme shell (scsh), Up: Guile Modules

6.15 Tracing

The (ice-9 debug) module implements tracing of procedure applications. When a procedure is traced, it means that every call to that procedure is reported to the user during a program run. The idea is that you can mark a collection of procedures for tracing, and Guile will subsequently print out a line of the form

     |  |  [procedure args ...]

whenever a marked procedure is about to be applied to its arguments. This can help a programmer determine whether a function is being called at the wrong time or with the wrong set of arguments.

In addition, the indentation of the output is useful for demonstrating how the traced applications are or are not tail recursive with respect to each other. Thus, a trace of a non-tail recursive factorial implementation looks like this:

     [fact1 4]
     |  [fact1 3]
     |  |  [fact1 2]
     |  |  |  [fact1 1]
     |  |  |  |  [fact1 0]
     |  |  |  |  1
     |  |  |  1
     |  |  2
     |  6

While a typical tail recursive implementation would look more like this:

     [fact2 4]
     [facti 1 4]
     [facti 4 3]
     [facti 12 2]
     [facti 24 1]
     [facti 24 0]
— Scheme Procedure: trace procedure

Enable tracing for procedure. While a program is being run, Guile will print a brief report at each call to a traced procedure, advising the user which procedure was called and the arguments that were passed to it.

— Scheme Procedure: untrace procedure

Disable tracing for procedure.

Here is another example:

     (define (rev ls)
       (if (null? ls)
           (append (rev (cdr ls))
                   (cons (car ls) '())))) ⇒ rev
     (trace rev) ⇒ (rev)
     (rev '(a b c d e))
     ⇒ [rev (a b c d e)]
        |  [rev (b c d e)]
        |  |  [rev (c d e)]
        |  |  |  [rev (d e)]
        |  |  |  |  [rev (e)]
        |  |  |  |  |  [rev ()]
        |  |  |  |  |  ()
        |  |  |  |  (e)
        |  |  |  (e d)
        |  |  (e d c)
        |  (e d c b)
        (e d c b a)
        (e d c b a)

Note the way Guile indents the output, illustrating the depth of execution at each procedure call. This can be used to demonstrate, for example, that Guile implements self-tail-recursion properly:

     (define (rev ls sl)
       (if (null? ls)
           (rev (cdr ls)
                (cons (car ls) sl)))) ⇒ rev
     (trace rev) ⇒ (rev)
     (rev '(a b c d e) '())
     ⇒ [rev (a b c d e) ()]
        [rev (b c d e) (a)]
        [rev (c d e) (b a)]
        [rev (d e) (c b a)]
        [rev (e) (d c b a)]
        [rev () (e d c b a)]
        (e d c b a)
        (e d c b a)

Since the tail call is effectively optimized to a goto statement, there is no need for Guile to create a new stack frame for each iteration. Tracing reveals this optimization in operation.