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:
parent
5c43d9c783
commit
42cb9b0311
1 changed files with 92 additions and 206 deletions
|
@ -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
|
||||
|
@ -243,13 +194,11 @@ output.
|
|||
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.
|
||||
@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
|
||||
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{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
|
||||
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,10 +474,10 @@ 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
|
||||
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
|
||||
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue