axiom-developer
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Axiom-developer] Re: lsp/Makefile.pamphlet


From: root
Subject: [Axiom-developer] Re: lsp/Makefile.pamphlet
Date: Wed, 15 Dec 2004 00:40:58 -0500

Yes, I just found that also. My mistake. Thanks.

You may find this useful. I'm going to add it to the sources:

\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{\$SPAD/src/interp debugfile.lisp}
\author{Timothy Daly}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{Axiom debugging}
In order to debug Axiom it is sometimes necessary to trace lisp functions.
This is tedious in the extreme. There are thousands of functions and the
stack gets very deep so finding function names (which are case-sensitive)
is time consuming.

In order to speed up this process there are a couple useful things to know.

First, the top level loop of Axiom will allow you to execute any lisp
function by typing:
\begin{verbatim}
)lisp (function arg arg...)
\end{verbatim}
This also quickly gets tedious. A slightly faster method is to do the
following:
\begin{verbatim}
)lisp (setq $dalymode t)
\end{verbatim}
This will change the top level read loop semantics so that any 
expression that starts with an open-paren is interpreted as lisp code.
Thus:
\begin{verbatim}
(1) -> (1 + 1)
   2
                         Type: PositiveInteger
(2) -> )lisp (setq $dalymode t)
Value = T

(2) -> (+ 1 1)
Value = 2

(2) ->
\end{verbatim}
Notice that the lisp expressions are not seen by the Axiom interpreter
and thus the expression numbers do not change.

It is also useful to know that when a lisp error occurs Axiom gives
you the choice of entering a lisp break loop or returning back to
the Axiom top level. The default is to return to the top level.
This can be changed by:
\begin{verbatim}
(1) -> 1/0
  >> Error detected within library code:
  division by zero
 
(1) -> )set break break
(1) -> 1/0
  >> Error detected within library code:
  division by zero
 
protected-symbol-warn called with (NIL)

Break.
Broken at FUNCALL. Type :H for Help.
BOOT>>:q
(1) ->
\end{verbatim}


The code in this file is useful for debugging the interpreter. If you
have a problem with a function, say [[|compColon|]], we find out what
file contains the code. In this case it is 
[[src/interp/compiler.boot.pamphlet]]. All of the pamphlet files for
the interpreter from the [[src/interp]] subdirectory will be extracted
as lisp source code in the [[int/interp]] subdirectory. Thus we want
the file [[int/interp/compiler.clisp]]. (Generally speaking [[.boot]]
files become [[.clisp]] files, for historical reasons).

The code in this file will allow you to trace every function in this
file with one command. There are two considerations. 

The first consideration is whether you want to trace compiled or
interpreted code.

If you want to trace the compiled code then just call the 
[[trace-file*]] functions. These functions expect a filename,
read the file looking for [[defuns]] and construct a proper call
to the [[trace]] macro.

If you want to load the interpreted code and then trace it call the
[[load-trace-file*]] functions.

The second consideration is whether you want regular trace output
or "noisy" trace output. 

The regular trace output uses the standard [[trace]] macro.

The "noisy" trace output uses [[:entry]] and [[:exit]] keywords
to attach output format functions.

So to trace interpreted [[compiler.boot]] functions you would:
\begin{verbatim}
)lisp (load-trace-file "int/interp/compiler.clisp")
\end{verbatim}
or the noisy version:
\begin{verbatim}
)lisp (load-trace-file-noisy "int/interp/compiler.clisp")
\end{verbatim}

To trace compiled [[compiler.boot]] functions you would:
\begin{verbatim}
)lisp (trace-file "int/interp/compiler.clisp")
\end{verbatim}
or the noisy version:
\begin{verbatim}
)lisp (trace-file-noisy "int/interp/compiler.clisp")
\end{verbatim}

\section{The trace function in GCL}
Example of tracing a function "fact" so that only the outermost call is traced.
\begin{verbatim}
(defun fact (n) (if (= n 0) 1 (* n (fact (1- n)))))

(defvar in-fact nil)

(trace (fact :declarations ((in-fact nil))
             :cond
             (null in-fact)
             :entry
             (progn
               (setq in-fact t)
               (princ "Here comes input ")
               (cons 'fact arglist))
             :exit
             (progn (setq in-fact nil)
                    (princ "Here comes output ")
                    (cons 'fact values))))
\end{verbatim}
Example of tracing fact so that only three levels are traced
\begin{verbatim}
(trace (fact :declarations
             ((fact-depth 0))
             :cond
             (and (< fact-depth 3)
                  (setq fact-depth (1+ fact-depth)))
             :exit
             (progn (setq fact-depth (1- fact-depth)) (cons 'fact values))))
\end{verbatim}
\section{load-trace-file}
The trace-file function expects a source file as its argument.
It reads the lisp forms in the file looking for and remembering
the package name. It looks for every [[defun]] form in the file
and wraps a call to [[trace]] around the form. The [[trace-file]]
function assumes that the file has already been loaded.

The load-trace-file function will load the file first. This is 
useful because you will get interpreted forms which gives more
debugging information.
<<load-trace-file>>=
(defun load-trace-file (file)
 (load file)
 (trace-file file))

(defun trace-file (file)
 "hang a trace call on all of the defuns in a file"
 (let (expr (package "BOOT"))
  (format t "monitoring ~s~%" file)
  (with-open-file (in file)
   (catch 'done
    (loop
     (setq expr (read in nil 'done))
     (when (eq expr 'done) (throw 'done nil))
     (if (and (consp expr) (eq (car expr) 'in-package))
      (if (and (consp (second expr)) (eq (first (second expr)) 'quote))
       (setq package (string (second (second expr))))
       (setq package (second expr)))
      (when (and (consp expr) (eq (car expr) 'defun))
       (print (list 'trace (intern (string (second expr)) package)))
       (eval (list 'trace (intern (string (second expr)) package) )))))))))

@
\section{load-trace-file-noisy}
The [[trace-file-noisy]] function expects a source file as its argument.
It reads the lisp forms in the file looking for and remembering
the package name. It looks for every [[defun]] form in the file
and wraps a call to [[trace]] around the form. The [[trace-file-noisy]]
function assumes that the file has already been loaded.

The GCL [[trace]] macro accepts keyword arguments. We exploit that
fact to give a readable execution trace. The trace will look something
like:
\begin{verbatim}
1> (:ENTER firstFunction (arg arg arg...))
  2> (:ENTER secondFunction (arg arg arg...))
    3> (:ENTER thirdFunction (arg arg arg...))
    3> (:EXIT thirdFunction (result result ...))
  2> (:EXIT secondFunction (result result ...))
1> (:EXIT firstFunction (result result ...))
\end{verbatim}
It may be useful to define your own entry and exit forms.
On entry the GCL [[trace]] macro wraps a [[let]] form that 
binds the variable [[si::arglist]] to the list of arguments. 
On exit the [[trace]] macro binds [[si::values]].

The load-trace-file-noisy function will load the file first. This is 
useful because you will get interpreted forms which gives more
debugging information.
<<load-trace-file-noisy>>=
(defun load-trace-file-noisy (file)
 (load file)
 (trace-file-noisy file))

(defun trace-file-noisy (file)
 "hang a trace call on all of the defuns in a file"
 (let (expr (package "BOOT"))
  (format t "monitoring ~s~%" file)
  (with-open-file (in file)
   (catch 'done
    (loop
     (setq expr (read in nil 'done))
     (when (eq expr 'done) (throw 'done nil))
     (if (and (consp expr) (eq (car expr) 'in-package))
      (if (and (consp (second expr)) (eq (first (second expr)) 'quote))
       (setq package (string (second (second expr))))
       (setq package (second expr)))
      (when (and (consp expr) (eq (car expr) 'defun))
       (print 
         (list 
          'trace (list (intern (string (second expr)) package) 
            :entry 
             (list 'list ':enter (list 'quote (second expr)) 'si::arglist)
            :exit  
             (list 'list ':exit (list 'quote (second expr)) 'si::values))))
       (eval 
        (list 
         'trace (list (intern (string (second expr)) package) 
           :entry 
            (list 'list ':enter (list 'quote (second expr)) 'si::arglist)
           :exit  
            (list 'list ':exit (list 'quote (second expr)) 'si::values))))
      )))))))

@
\section{fast-links}
GCL optimizes function calling in a reversible way. The first call to
a function will set up a fast calling link. This feature is on by default.
It considerably speeds up function calling. If the function is recompiled,
traced, or redefined this link is undone. In order to get better debugging
information we turn this feature off.
<<fast-links>>=
(si::use-fast-links nil)

@
<<*>>=
<<fast-links>>
<<load-trace-file>>
<<load-trace-file-noisy>>
@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]