[gambit-list] Debugging techniques

Marc Feeley feeley at iro.umontreal.ca
Fri Oct 7 09:13:34 EDT 2016


Tail-calls are not an optimization in Scheme, they are required by the semantics of the language.  This gives a behavior at run time that is surprizing for programmers used to other languages where all calls are handled the same, i.e. the caller’s activation frame is kept on the stack for the duration of the call to the callee even for tail-calls.  So the way tail-calls are handled in Scheme are an “optimization” only when compared to these other languages.

For any compiler, optimizations can get in the way of debugging because some contextual information may be missing if the compiler determines that this information is not needed at run time.  In addition to removing the parent activation frame on tail-calls, the Gambit compiler removes from the activation frame any variable that has become “dead” (a variable whose value won’t be used in the rest of the function).  This is actually related to the problem of space leakage and the “safe for space” property.  To have more information available when debugging the general idea is to disable these optimizations.

Here’s an example of debugging a tail-recursive function f:

% cat tc.scm
(define (f n)
  (if (= n 0)
      (/ 1 n)
      (f (- n 1))))

(f 5)

% gsc -debug tc.scm
% gsi -:dar tc
*** ERROR IN ##load -- Divide by zero
(/ 1 0)
> ,be
0  ##load                  
1  ##main                  

As you can see all the activation frames have been removed from the stack.

Adding these declarations to the top of tc.scm will disable the optimizations:

(declare
  (not proper-tail-calls) ;; don't optimize tail calls
  (not optimize-dead-local-variables) ;; don't remove dead variables
)

Now we get:

% gsc -debug tc.scm
% gsi -:dar tc
*** ERROR IN f, "tc.scm"@8.7 -- Divide by zero
(/ 1 0)
> ,be
0  f                       "tc.scm"@8:7    (/ 1 n)
        n == 0
1  f                       "tc.scm"@9:7    (f (- n 1))
        n == 1
2  f                       "tc.scm"@9:7    (f (- n 1))
        n == 2
3  f                       "tc.scm"@9:7    (f (- n 1))
        n == 3
4  f                       "tc.scm"@9:7    (f (- n 1))
        n == 4
5  f                       "tc.scm"@9:7    (f (- n 1))
        n == 5
6  | tc|                   "tc.scm"@11:1   (f 5)
7  ##load                  
8  ##main                  

Note that the declarations don’t have to be global.  They can be put inside the functions where you want to disable the optimizations.

Hope that helps!

Marc

> On Oct 7, 2016, at 2:00 AM, Vijay Mathew <vijay.the.lisper at gmail.com> wrote:
> 
> As the compiler optimizes the (tail-call) stack away, getting to the root cause of an error 
> can sometimes be a challenge in Scheme.
> 
> It would be great if the more experienced members in the Gambit community can share 
> the techniques they use to debug programs, especially in production.
> 
> Thanks,
> 
> --Vijay
> _______________________________________________
> Gambit-list mailing list
> Gambit-list at iro.umontreal.ca
> https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list



More information about the Gambit-list mailing list