1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

Update api-debug.texi; there is a ways to go.

* doc/ref/api-debug.texi: Update a bit.
This commit is contained in:
Andy Wingo 2010-03-14 23:05:42 +01:00
parent 5c43d9c783
commit 42cb9b0311

View file

@ -10,9 +10,9 @@
@cindex Debugging
In order to understand Guile's debugging facilities, you first need to
understand a little about how the evaluator works and what the Scheme
stack is. With that in place we explain the low level trap calls that
the evaluator can be configured to make, and the trap and breakpoint
understand a little about how Guile represent the Scheme control stack.
With that in place we explain the low level trap calls that the
evaluator can be configured to make, and the trap and breakpoint
infrastructure that builds on top of those calls.
@menu
@ -25,41 +25,31 @@ infrastructure that builds on top of those calls.
@node Evaluation Model
@subsection Evaluation and the Scheme Stack
The idea of the Scheme stack is central to a lot of debugging. It
always exists implicitly, as a result of the way that the Guile
evaluator works, and can be summoned into concrete existence as a
first-class Scheme value by the @code{make-stack} call, so that an
introspective Scheme program -- such as a debugger -- can present it in
some way and allow the user to query its details. The first thing to
understand, therefore, is how the workings of the evaluator build up the
stack.
The idea of the Scheme stack is central to a lot of debugging. The
Scheme stack is a reified representation of the pending function returns
in an expression's continuation. As Guile implements function calls
using a stack, this reification takes the form of a number of nested
stack frames, each of which has the procedure and its arguments, along
with local variables and temporary values.
@cindex Evaluations
@cindex Applications
Broadly speaking, the evaluator performs @dfn{evaluations} and
@dfn{applications}. An evaluation means that it is looking at a source
code expression like @code{(+ x 5)} or @code{(if msg (loop))}, deciding
whether the top level of the expression is a procedure call, macro,
builtin syntax, or whatever, and doing some appropriate processing in
each case. (In the examples here, @code{(+ x 5)} would normally be a
procedure call, and @code{(if msg (loop))} builtin syntax.) For a
procedure call, ``appropriate processing'' includes evaluating the
procedure's arguments, as that must happen before the procedure itself
can be called. An application means calling a procedure once its
arguments have been calculated.
A Scheme stack always exists implicitly, and can be summoned into
concrete existence as a first-class Scheme value by the
@code{make-stack} call, so that an introspective Scheme program -- such
as a debugger -- can present it in some way and allow the user to query
its details. The first thing to understand, therefore, is how Guile's
function call convention creates the stack.
@cindex Stack
@cindex Frames
@cindex Stack frames
Typically evaluations and applications alternate with each other, and
together they form a @dfn{stack} of operations pending completion. This
is because, on the one hand, evaluation of an expression like @code{(+ x
5)} requires --- once its arguments have been calculated --- an
application (in this case, of the procedure @code{+}) before it can
complete and return a result, and, on the other hand, the application of
a procedure written in Scheme involves evaluating the sequence of
expressions that constitute that procedure's code. Each level on this
stack is called a @dfn{frame}.
Broadly speaking, Guile represents all control flow on a stack. Calling
a function involves pushing an empty frame on the stack, then evaluating
the procedure and its arguments, then fixing up the new frame so that it
points to the old one. Frames on the stack are thus linked together. A
tail call is the same, except it reuses the existing frame instead of
pushing on a new one.
In this way, the only frames that are on the stack are ``active''
frames, frames which need to do some work before the computation is
complete. On the other hand, a function that has tail-called another
function will not be on the stack, as it has no work left to do.
Therefore, when an error occurs in a running program, or the program
hits a breakpoint, or in fact at any point that the programmer chooses,
@ -74,7 +64,6 @@ stack and its frames.
* Examining the Stack::
* Examining Stack Frames::
* Source Properties:: Remembering the source of an expression.
* Decoding Memoized Source Expressions::
* Starting a New Stack::
@end menu
@ -96,10 +85,10 @@ describes the Scheme stack at that point.
Create a new stack. If @var{obj} is @code{#t}, the current
evaluation stack is used for creating the stack frames,
otherwise the frames are taken from @var{obj} (which must be
either a debug object or a continuation).
a continuation or a frame object).
@var{args} should be a list containing any combination of
integer, procedure and @code{#t} values.
integer, procedure, prompt tag and @code{#t} values.
These values specify various ways of cutting away uninteresting
stack frames from the top and bottom of the stack that
@ -107,28 +96,26 @@ stack frames from the top and bottom of the stack that
@code{(@var{inner_cut_1} @var{outer_cut_1} @var{inner_cut_2}
@var{outer_cut_2} @dots{})}.
Each @var{inner_cut_N} can be @code{#t}, an integer, or a
procedure. @code{#t} means to cut away all frames up to but
excluding the first user module frame. An integer means to cut
away exactly that number of frames. A procedure means to cut
away all frames up to but excluding the application frame whose
procedure matches the specified one.
Each @var{inner_cut_N} can be @code{#t}, an integer, a prompt
tag, or a procedure. @code{#t} means to cut away all frames up
to but excluding the first user module frame. An integer means
to cut away exactly that number of frames. A prompt tag means
to cut away all frames that are inside a prompt with the given
tag. A procedure means to cut away all frames up to but
excluding the application frame whose procedure matches the
specified one.
Each @var{outer_cut_N} can be an integer or a procedure. An
integer means to cut away that number of frames. A procedure
means to cut away frames down to but excluding the application
frame whose procedure matches the specified one.
Each @var{outer_cut_N} can be an integer, a prompt tag, or a
procedure. An integer means to cut away that number of frames.
A prompt tag means to cut away all frames that are outside a
prompt with the given tag. A procedure means to cut away
frames down to but excluding the application frame whose
procedure matches the specified one.
If the @var{outer_cut_N} of the last pair is missing, it is
taken as 0.
@end deffn
@deffn {Scheme Procedure} last-stack-frame obj
@deffnx {C Function} scm_last_stack_frame (obj)
Return the last (innermost) frame of @var{obj}, which must be
either a debug object or a continuation.
@end deffn
@node Examining the Stack
@subsubsection Examining the Stack
@ -175,33 +162,12 @@ backtrace.
Return @code{#t} if @var{obj} is a stack frame.
@end deffn
@deffn {Scheme Procedure} frame-number frame
@deffnx {C Function} scm_frame_number (frame)
Return the frame number of @var{frame}.
@end deffn
@deffn {Scheme Procedure} frame-previous frame
@deffnx {C Function} scm_frame_previous (frame)
Return the previous frame of @var{frame}, or @code{#f} if
@var{frame} is the first frame in its stack.
@end deffn
@deffn {Scheme Procedure} frame-next frame
@deffnx {C Function} scm_frame_next (frame)
Return the next frame of @var{frame}, or @code{#f} if
@var{frame} is the last frame in its stack.
@end deffn
@deffn {Scheme Procedure} frame-source frame
@deffnx {C Function} scm_frame_source (frame)
Return the source of @var{frame}.
@end deffn
@deffn {Scheme Procedure} frame-procedure? frame
@deffnx {C Function} scm_frame_procedure_p (frame)
Return @code{#t} if a procedure is associated with @var{frame}.
@end deffn
@deffn {Scheme Procedure} frame-procedure frame
@deffnx {C Function} scm_frame_procedure (frame)
Return the procedure for @var{frame}, or @code{#f} if no
@ -213,21 +179,6 @@ procedure is associated with @var{frame}.
Return the arguments of @var{frame}.
@end deffn
@deffn {Scheme Procedure} frame-evaluating-args? frame
@deffnx {C Function} scm_frame_evaluating_args_p (frame)
Return @code{#t} if @var{frame} contains evaluated arguments.
@end deffn
@deffn {Scheme Procedure} frame-overflow? frame
@deffnx {C Function} scm_frame_overflow_p (frame)
Return @code{#t} if @var{frame} is an overflow frame.
@end deffn
@deffn {Scheme Procedure} frame-real? frame
@deffnx {C Function} scm_frame_real_p (frame)
Return @code{#t} if @var{frame} is a real frame.
@end deffn
@deffn {Scheme Procedure} display-application frame [port [indent]]
@deffnx {C Function} scm_display_application (frame, port, indent)
Display a procedure application @var{frame} to the output port
@ -242,14 +193,12 @@ output.
@cindex source properties
As Guile reads in Scheme code from file or from standard input, it
remembers the file name, line number and column number where each
expression begins. These pieces of information are known as the
@dfn{source properties} of the expression. If an expression undergoes
transformation --- for example, if there is a syntax transformer in
effect, or the expression is a macro call --- the source properties are
copied from the untransformed to the transformed expression so that, if
an error occurs when evaluating the transformed expression, Guile's
debugger can point back to the file and location where the expression
originated.
expression begins. These pieces of information are known as the
@dfn{source properties} of the expression. Syntax expanders and the
compiler propagate these source properties to compiled procedures, so
that, if an error occurs when evaluating the transformed expression,
Guile's debugger can point back to the file and location where the
expression originated.
The way that source properties are stored means that Guile can only
associate source properties with parenthesized expressions, and not, for
@ -275,10 +224,7 @@ port>''.
The recording of source properties is controlled by the read option
named ``positions'' (@pxref{Reader options}). This option is switched
@emph{on} by default, together with the debug options ``debug'' and
``backtrace'' (@pxref{Debugger options}), when Guile is run
interactively; all these options are @emph{off} by default when Guile
runs a script non-interactively.
@emph{on} by default.
The following procedures can be used to access and set the source
properties of read expressions.
@ -306,52 +252,9 @@ Return the property specified by @var{key} from @var{obj}'s source
properties.
@end deffn
In practice there are only two ways that you should use the ability to
set an expression's source properties.
@itemize
@item
To set a breakpoint on an expression, use @code{(set-source-property!
@var{expr} 'breakpoint #t)}. If you do this, you should also set the
@code{traps} and @code{enter-frame-handler} trap options
(@pxref{Evaluator trap options}) and @code{breakpoints} debug option
(@pxref{Debugger options}) appropriately, and the evaluator will then
call your enter frame handler whenever it is about to evaluate that
expression.
@item
To make a read or constructed expression appear to have come from a
different source than what the expression's source properties already
say, you can use @code{set-source-property!} to set the expression's
@code{filename}, @code{line} and @code{column} properties. The
properties that you set will then show up later if that expression is
involved in a backtrace or error report.
@end itemize
If you are looking for a way to attach arbitrary information to an
expression other than these properties, you should use
@code{make-object-property} instead (@pxref{Object Properties}). That
will avoid bloating the source property hash table, which is really
only intended for the debugging purposes just described.
@node Decoding Memoized Source Expressions
@subsubsection Decoding Memoized Source Expressions
@deffn {Scheme Procedure} memoized? obj
@deffnx {C Function} scm_memoized_p (obj)
Return @code{#t} if @var{obj} is memoized.
@end deffn
@deffn {Scheme Procedure} unmemoize m
@deffnx {C Function} scm_unmemoize (m)
Unmemoize the memoized expression @var{m},
@end deffn
@deffn {Scheme Procedure} memoized-environment m
@deffnx {C Function} scm_memoized_environment (m)
Return the environment of the memoized expression @var{m}.
@end deffn
If the @code{positions} reader option is enabled, each parenthesized
expression will have values set for the @code{filename}, @code{line} and
@code{column} properties.
@node Starting a New Stack
@ -377,15 +280,15 @@ the error chose explicitly to provide. This information originates with
the @code{error} or @code{throw} call (or their C code equivalents, if
the error is detected by C code) that signals the error, and is passed
automatically to the handler procedure of the innermost applicable
@code{catch}, @code{lazy-catch} or @code{with-throw-handler} expression.
@code{catch} or @code{with-throw-handler} expression.
@subsubsection Intercepting basic error information
Therefore, to catch errors that occur within a chunk of Scheme code, and
to intercept basic information about those errors, you need to execute
that code inside the dynamic context of a @code{catch},
@code{lazy-catch} or @code{with-throw-handler} expression, or the
equivalent in C. In Scheme, this means you need something like this:
that code inside the dynamic context of a @code{catch} or
@code{with-throw-handler} expression, or the equivalent in C. In Scheme,
this means you need something like this:
@lisp
(catch #t
@ -436,9 +339,8 @@ SCM my_handler_proc (void *handler_data,
@noindent
Again, as with the Scheme version, @code{scm_c_catch} could be replaced
by @code{scm_internal_lazy_catch} or @code{scm_c_with_throw_handler},
and @code{SCM_BOOL_T} could instead be the symbol for a particular kind
of error.
by @code{scm_c_with_throw_handler}, and @code{SCM_BOOL_T} could instead
be the symbol for a particular kind of error.
@subsubsection Capturing the full error stack
@ -446,19 +348,10 @@ The other interesting information about an error is the full Scheme
stack at the point where the error occurred; in other words what
innermost expression was being evaluated, what was the expression that
called that one, and so on. If you want to write your code so that it
captures and can display this information as well, there are three
captures and can display this information as well, there are a couple
important things to understand.
Firstly, the code in question must be executed using the debugging
version of the evaluator, because information about the Scheme stack is
only available at all from the debugging evaluator. Using the debugging
evaluator means that the debugger option (@pxref{Debugger options})
called @code{debug} must be enabled; this can be done by running
@code{(debug-enable 'debug)} or @code{(turn-on-debugging)} at the top
level of your program; or by running guile with the @code{--debug}
command line option, if your program begins life as a Scheme script.
Secondly, the stack at the point of the error needs to be explicitly
Firstly, the stack at the point of the error needs to be explicitly
captured by a @code{make-stack} call (or the C equivalent
@code{scm_make_stack}). The Guile library does not do this
``automatically'' for you, so you will need to write code with a
@ -472,16 +365,15 @@ running on top of the Guile library, and which uses @code{catch} and
@code{make-stack} in the way we are about to describe to capture the
stack when an error occurs.)
Thirdly, in order to capture the stack effectively at the point where
the error occurred, the @code{make-stack} call must be made before Guile
unwinds the stack back to the location of the prevailing catch
expression. This means that the @code{make-stack} call must be made
within the handler of a @code{lazy-catch} or @code{with-throw-handler}
expression, or the optional "pre-unwind" handler of a @code{catch}.
(For the full story of how these alternatives differ from each other,
see @ref{Exceptions}. The main difference is that @code{catch}
terminates the error, whereas @code{lazy-catch} and
@code{with-throw-handler} only intercept it temporarily and then allow
And secondly, in order to capture the stack effectively at the point
where the error occurred, the @code{make-stack} call must be made before
Guile unwinds the stack back to the location of the prevailing catch
expression. This means that the @code{make-stack} call must be made
within the handler of a @code{with-throw-handler} expression, or the
optional "pre-unwind" handler of a @code{catch}. (For the full story of
how these alternatives differ from each other, see @ref{Exceptions}. The
main difference is that @code{catch} terminates the error, whereas
@code{with-throw-handler} only intercepts it temporarily and then allow
it to continue propagating up to the next innermost handler.)
So, here are some examples of how to do all this in Scheme and in C.
@ -582,11 +474,11 @@ application frame -- that is, a frame that satisfies the
@subsubsection What the Guile REPL does
The Guile REPL code (in @file{ice-9/boot-9.scm}) uses a @code{catch}
with a pre-unwind handler to capture the stack when an error occurs in
an expression that was typed into the REPL, and saves the captured stack
in a fluid (@pxref{Fluids and Dynamic States}) called
@code{the-last-stack}. You can then use the @code{(backtrace)} command,
The Guile REPL code (in @file{system/repl/repl.scm} and related files)
uses a @code{catch} with a pre-unwind handler to capture the stack when
an error occurs in an expression that was typed into the REPL, and saves
the captured stack in a fluid (@pxref{Fluids and Dynamic States}) called
@code{the-last-stack}. You can then use the @code{(backtrace)} command,
which is basically equivalent to @code{(display-backtrace (fluid-ref
the-last-stack))}, to print out this stack at any time until it is
overwritten by the next error that occurs.
@ -619,17 +511,16 @@ Invoke the Guile debugger to explore the context of the last error.
@cindex Tracing
@cindex Code coverage
@cindex Profiling
The low level C code of Guile's evaluator can be configured to call
out at key points to arbitrary user-specified procedures. These
procedures, and the circumstances under which the evaluator calls
them, are configured by the ``evaluator trap options'' interface
(@pxref{Evaluator trap options}), and by the @code{trace} and
@code{breakpoints} fields of the ``debug options'' interface
(@pxref{Debugger options}). In principle this allows Scheme code to
implement any model it chooses for examining the evaluation stack as
program execution proceeds, and for suspending execution to be resumed
later. Possible applications of this feature include breakpoints,
runtime tracing, code coverage, and profiling.
Guile's virtual machine can be configured to call out at key points to
arbitrary user-specified procedures. For more information on these
hooks, and the circumstances under which the VM calls them, see @ref{VM
Behaviour}.
In principle, these hooks allow Scheme code to implement any model it
chooses for examining the evaluation stack as program execution
proceeds, and for suspending execution to be resumed later. Possible
applications of this feature include breakpoints, runtime tracing, code
coverage, and profiling.
@cindex Trap classes
@cindex Trap objects
@ -650,6 +541,14 @@ user wanting to use traps, and the developer interested in
understanding how the interface hangs together.
@subsubsection Actually, this section is bitrotten
Dear reader: the following sections have some great ideas, and some code
that just needs a few days of massaging to get it to work with the VM
(as opposed to the old interpreter). Want to help? Yes? Yes!
@code{guile-devel@@gnu.org}, that's where.
@subsubsection A Quick Note on Terminology
@cindex Trap terminology
@ -1886,19 +1785,6 @@ hi!
guile>
@end lisp
@anchor{Memoization}
@cindex Memoization
(For anyone wondering why the first @code{(do-main 4)} call above
generates lots more trace lines than the subsequent calls: these
examples also demonstrate how the Guile evaluator ``memoizes'' code.
When Guile evaluates a source code expression for the first time, it
changes some parts of the expression so that they will be quicker to
evaluate when that expression is evaluated again; this is called
memoization. The trace output from the first @code{(do-main 4)} call
shows memoization steps, such as an internal define being transformed to
a letrec.)
@c Local Variables:
@c TeX-master: "guile.texi"
@c End: