Version 1 (modified by bfulgham, 13 years ago) (diff)


Debug With OpenMCL

Tracing Recursive Functions

Problem: Is there a way to get trace to show recursive calls in addition to the outermost call? For example, if you trace fact as below, it only prints the call of (FACT 10 1). We would like to be able to see the call of (FACT 10 1), (FACT 9 10), (FACT 8 90), etc., all the way down

  ? (defun fact (x acc)
      (if (= x 0)
        (fact (- x 1) (* x acc))))

  ? (trace fact)

  ? (fact 10 1)
  0> Calling (FACT 10 1)
  <0 FACT returned 3628800

Solution: When a (globally named) function FOO is defined with DEFUN, the compiler is allowed to assume that functional references to that function name refer to the function being defined (unless they're lexically shadowed); it can therefore skip the implicit SYMBOL-FUNCTION ("call whatever is in the function cell of FOO") on a self-call (a call to FOO from within FOO.) This saves an instruction or two on those calls, but (since TRACE works by changing what SYMBOL-FUNCTION returns) those inlined self-calls can't be traced.

However, the compiler can't do this (can't even assume that something defined by DEFUN won't be redefined later) if the function name is declared NOTINLINE at the point of the self call:

(defun fact (n)         ; other traditional definition
  (declare (NOTINILINE FACT)) ; and the compiler can't ignore NOTINLINE
  (if (zerop n) 1 (* n (fact (1- n)))))

That's the way to say to the compiler, "as a matter of policy, I'd rather have the ability to trace functions which call themselves and are defined with DEFUN and don't care about saving a few cycles per self-call".

Alternate Solution: Note that the Clozure Common Lisp IDE permits the function to be traced as desired from within the IDE without needing to add the NOTINLINE command. So, if you work within the IDE you don't have to modify the sources at all.