diff options
author | Neil Jerram <neil@ossau.uklinux.net> | 2006-08-11 16:21:14 +0000 |
---|---|---|
committer | Neil Jerram <neil@ossau.uklinux.net> | 2006-08-11 16:21:14 +0000 |
commit | 62ae95577a503e89114584ae8571b2b128d79ca5 (patch) | |
tree | 370ebdfe3574f074537f98474f3b85426e841ef8 | |
parent | 5af872e136b2e7c74b566b68bbab97b288464578 (diff) | |
download | guile-62ae95577a503e89114584ae8571b2b128d79ca5.tar.gz |
* scheme-using.texi (Run To Frame Exit): Improved doc for finish.
(Continue Execution): Improved doc for continue.
(Using Guile in Emacs): Lots of new docs about the Emacs
interface.
* api-debug.texi (Low Level Trap Calls): New.
(Using Traps): Removed, material incorporated into Low Level Trap
Calls.
(High Level Traps): New.
(Breakpoints): New.
* scheme-using.texi (Single Stepping): Improve doc for step and
next.
* api-debug.texi (Debug on Error): Note need to handling of errors
in C.
-rw-r--r-- | doc/ref/ChangeLog | 17 | ||||
-rw-r--r-- | doc/ref/api-debug.texi | 1267 | ||||
-rw-r--r-- | doc/ref/scheme-using.texi | 783 |
3 files changed, 2058 insertions, 9 deletions
diff --git a/doc/ref/ChangeLog b/doc/ref/ChangeLog index 982871ee6..451c6f575 100644 --- a/doc/ref/ChangeLog +++ b/doc/ref/ChangeLog @@ -1,5 +1,22 @@ 2006-08-11 Neil Jerram <neil@ossau.uklinux.net> + * scheme-using.texi (Run To Frame Exit): Improved doc for finish. + (Continue Execution): Improved doc for continue. + (Using Guile in Emacs): Lots of new docs about the Emacs + interface. + + * api-debug.texi (Low Level Trap Calls): New. + (Using Traps): Removed, material incorporated into Low Level Trap + Calls. + (High Level Traps): New. + (Breakpoints): New. + + * scheme-using.texi (Single Stepping): Improve doc for step and + next. + + * api-debug.texi (Debug on Error): Note need to handling of errors + in C. + * api-debug.texi (Debugging): New intro text. New subsection "Evaluation Model". Moved existing subsections "Capturing the Stack or Innermost Stack Frame", "Examining the Stack", "Examining diff --git a/doc/ref/api-debug.texi b/doc/ref/api-debug.texi index 42527b7ec..6868ef211 100644 --- a/doc/ref/api-debug.texi +++ b/doc/ref/api-debug.texi @@ -17,7 +17,9 @@ infrastructure that builds on top of those calls. @menu * Evaluation Model:: Evaluation and the Scheme stack. * Debug on Error:: Debugging when an error occurs. -* Using Traps:: +* Low Level Trap Calls:: +* High Level Traps:: +* Breakpoints:: @end menu @node Evaluation Model @@ -380,9 +382,48 @@ the backtrace. Invoke the Guile debugger to explore the context of the last error. @end deffn - -@node Using Traps -@subsection Using Traps +[Should also cover how to catch and debug errors from C, including +discussion of lazy/pre-unwind handlers.] + + +@node Low Level Trap Calls +@subsection Low Level Trap Calls + +@cindex Low level trap calls +@cindex Evaluator trap calls +Guile's evaluator can be configured to call three user-specified +procedures at various points in its operation: an +@dfn{apply-frame-handler} procedure, an @dfn{enter-frame-handler} +procedure, and an @dfn{exit-frame-handler} procedure. 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}). + +It is not necessary to understand the fine details of these low level +calls, and of the options which configure them, in order to use the +class-based trap interface effectively. @code{guile-debugging} takes +care of setting these options as required for whatever set of +installed trap objects the user specifies.@footnote{And consequently, +when using the class-based trap interface, users/applications should +@emph{not} modify these options themselves, to avoid interfering with +@code{guile-debugging}'s option settings.} It is useful, though, to +have a overall idea of how the evaluator works and when these low +level calls can happen, as follows. + +@cindex Frame entry +@cindex Frame exit +On the basis of this description, we can now specify the points where +low level trap calls may occur (subject to configuration). Namely, +whenever a new frame is added to the stack, because the evaluator is +about to begin a new evaluation or to perform a new application, and +whenever a frame is being removed from the stack because the +computation that it refers to has completed and is returning its +value@footnote{If this raises the question of how expressions with +no return value are handled, the answer is that all computations in +Guile return a value. Those that appear to have no return value do so +by using the special @code{*unspecified*} value, which the Guile REPL +avoids displaying to the user.} to its caller. @deffn {Scheme Procedure} with-traps thunk @deffnx {C Function} scm_with_traps (thunk) @@ -395,6 +436,1224 @@ Return @code{#t} if @var{obj} is a debug object. @end deffn +@node High Level Traps +@subsection High Level Traps + +@cindex Traps +@cindex Evaluator trap calls +@cindex Breakpoints +@cindex Trace +@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 code. 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. + +@cindex Trap classes +@cindex Trap objects +Based on these low level trap calls, the enhancements described here +provide a much higher level, object-oriented interface for the +manipulation of traps. Different kinds of trap are represented as +GOOPS classes; for example, the @code{<procedure-trap>} class +describes traps that are triggered by invocation of a specified +procedure. A particular instance of a trap class --- or @dfn{trap +object} --- describes the condition under which a single trap will be +triggered, and what will happen then; for example, an instance of +@code{<procedure-trap>} whose @code{procedure} and @code{behaviour} +slots contain @code{my-factorial} and @code{debug-trap} would be a +trap that enters the command line debugger when the +@code{my-factorial} procedure is invoked. + +The following subsubsections describe all this in greater detail, for both +the user wanting to use traps, and the developer interested in +understanding how the interface hangs together. + + +@subsubsection A Quick Note on Terminology + +@cindex Trap terminology +It feels natural to use the word ``trap'' in some form for all levels +of the structure just described, so we need to be clear on the +terminology we use to describe each particular level. The terminology +used in this subsection is as follows. + +@itemize @bullet +@item +@cindex Evaluator trap calls +@cindex Low level trap calls +``Low level trap calls'', or ``low level traps'', are the calls made +directly from the C code of the Guile evaluator. + +@item +@cindex Trap classes +``Trap classes'' are self-explanatory. + +@item +@cindex Trap objects +``Trap objects'', ``trap instances'', or just ``traps'', are instances +of a trap class, and each describe a single logical trap condition +plus behaviour as specified by the user of this interface. +@end itemize + +A good example of when it is important to be clear, is when we talk +below of behaviours that should only happen once per low level trap. +A single low level trap call will typically map onto the processing of +several trap objects, so ``once per low level trap'' is significantly +different from ``once per trap''. + + +@menu +* How to Set a Trap:: +* Specifying Trap Behaviour:: +* Trap Context:: +* Tracing Examples:: +* Tracing Configuration:: +* Tracing and (ice-9 debug):: +* Traps Installing More Traps:: +* Common Trap Options:: +* Procedure Traps:: +* Exit Traps:: +* Entry Traps:: +* Apply Traps:: +* Step Traps:: +* Source Traps:: +* Location Traps:: +* Trap Shorthands:: +* Trap Utilities:: +@end menu + + +@node How to Set a Trap +@subsubsection How to Set a Trap + +@cindex Setting traps +@cindex Installing and uninstalling traps +Setting a trap is done in two parts. First the trap is defined by +creating an instance of the appropriate trap class, with slot values +specifying the condition under which the trap will fire and the action +to take when it fires. Secondly the trap object thus created must be +@dfn{installed}. + +To make this immediately concrete, here is an example that sets a trap +to fire on the next application of the @code{facti} procedure, and to +handle the trap by entering the command line debugger. + +@lisp +(install-trap (make <procedure-trap> + #:procedure facti + #:single-shot #t + #:behaviour debug-trap)) +@end lisp + +@noindent +Briefly, the elements of this incantation are as follows. (All of +these are described more fully in the following subsubsections.) + +@itemize @bullet +@item +@code{<procedure-trap>} is the trap class for trapping on invocation +of a specific procedure. + +@item +@code{#:procedure facti} says that the specific procedure to trap on for this +trap object is @code{facti}. + +@item +@code{#:single-shot #t} says that this trap should only fire on the +@emph{next} invocation of @code{facti}, not on all future invocations +(which is the default if the @code{#:single-shot} option is not +specified). + +@item +@code{#:behaviour debug-trap} says that the trap infrastructure should +call the procedure @code{debug-trap} when this trap fires. + +@item +Finally, the @code{install-trap} call installs the trap immediately. +@end itemize + +@noindent +It is of course possible for the user to define more convenient +shorthands for setting common kinds of traps. @xref{Trap Shorthands}, +for some examples. + +The ability to install, uninstall and reinstall a trap without losing +its definition is @code{guile-debugging}'s equivalent of the +disable/enable commands provided by debuggers like GDB. + +@deffn {Generic Function} install-trap trap +Install the trap object @var{trap}, so that its behaviour will be +executed when the conditions for the trap firing are met. +@end deffn + +@deffn {Generic Function} uninstall-trap trap +Uninstall the trap object @var{trap}, so that its behaviour will +@emph{not} be executed even if the conditions for the trap firing are +met. +@end deffn + + +@node Specifying Trap Behaviour +@subsubsection Specifying Trap Behaviour + +@cindex Trap behaviour +@code{guile-debugging} provides several ``out-of-the-box'' behaviours +for common needs. All of the following can be used directly as the +value of the @code{#:behaviour} option when creating a trap object. + +@deffn {Procedure} debug-trap trap-context +Enter Guile's command line debugger to explore the stack at +@var{trap-context}, and to single-step or continue program execution +from that point. +@end deffn + +@deffn {Procedure} gds-debug-trap trap-context +Use the GDS debugging interface, which displays the stack and +corresponding source code via Emacs, to explore the stack at +@var{trap-context} and to single-step or continue program execution +from that point. +@end deffn + +@cindex Trace +@cindex Tracing +@deffn {Procedure} trace-trap trap-context +Display trace information to summarize the current @var{trap-context}. +@end deffn + +@deffn {Procedure} trace-at-exit trap-context +Install a further trap to cause the return value of the application or +evaluation just starting (as described by @var{trap-context}) to be +traced using @code{trace-trap}, when this application or evaluation +completes. The extra trap is automatically uninstalled after the +return value has been traced. +@end deffn + +@deffn {Procedure} trace-until-exit trap-context +Install a further trap so that every step that the evaluator performs +as part of the application or evaluation just starting (as described +by @var{trap-context}) is traced using @code{trace-trap}. The extra +trap is automatically uninstalled when the application or evaluation +is complete. @code{trace-until-exit} can be very useful as a first +step when all you know is that there is a bug ``somewhere in XXX or in +something that XXX calls''. +@end deffn + +@noindent +@code{debug-trap} and @code{gds-debug-trap} are provided by the modules +@code{(ice-9 debugger)} and @code{(ice-9 gds-client)} respectively, and +their behaviours are fairly self-explanatory. For more information on +the operation of the GDS interface via Emacs, see @ref{Using Guile in +Emacs}. The tracing behaviours are explained more fully below. + +@cindex Trap context +More generally, the @dfn{behaviour} specified for a trap can be any +procedure that expects to be called with one @dfn{trap context} +argument. A trivial example would be: + +@lisp +(define (report-stack-depth trap-context) + (display "Stack depth at the trap is: ") + (display (tc:depth trap-context)) + (newline)) +@end lisp + + +@node Trap Context +@subsubsection Trap Context + +The @dfn{trap context} is an object that caches information about the +low level trap call and the stack at the point of the trap, and is +passed as the only argument to all behaviour procedures. The +information in the trap context can be accessed through the procedures +beginning @code{tc:} that are exported by the @code{(ice-9 debugging +traps)} module@footnote{Plus of course any procedures that build on +these, such as the @code{trace/@dots{}} procedures exported by +@code{(ice-9 debugging trace)} (@pxref{Tracing Configuration}).}; the +most useful of these are as follows. + +@deffn {Generic Function} tc:type trap-context +Indicates the type of the low level trap by returning one of the +keywords @code{#:application}, @code{#:evaluation}, @code{#:return} or +@code{#:error}. +@end deffn + +@deffn {Generic Function} tc:return-value trap-context +When @code{tc:type} gives @code{#:return}, this provides the value +that is being returned. +@end deffn + +@deffn {Generic Function} tc:stack trap-context +Provides the stack at the point of the trap (as computed by +@code{make-stack}, but cached so that the lengthy @code{make-stack} +operation is not performed more than once for the same low level +trap). +@end deffn + +@deffn {Generic Function} tc:frame trap-context +The innermost frame of the stack at the point of the trap. +@end deffn + +@deffn {Generic Function} tc:depth trap-context +The number of frames (including tail recursive non-real frames) in the +stack at the point of the trap. +@end deffn + +@deffn {Generic Function} tc:real-depth trap-context +The number of real frames (that is, excluding the non-real frames that +describe tail recursive calls) in the stack at the point of the trap. +@end deffn + + +@node Tracing Examples +@subsubsection Tracing Examples + +The following examples show what tracing is and the kind of output that +it generates. In the first example, we define a recursive function for +reversing a list, then watch the effect of the recursive calls by +tracing each call and return value. + +@lisp +guile> (define (rev ls) + (if (null? ls) + ls + (append (rev (cdr ls)) + (list (car ls))))) +guile> (use-modules (ice-9 debugging traps) (ice-9 debugging trace)) +guile> (define t1 (make <procedure-trap> + #:procedure rev + #:behaviour (list trace-trap + trace-at-exit))) +guile> (install-trap t1) +guile> (rev '(a b c)) +| 2: [rev (a b c)] +| 3: [rev (b c)] +| 4: [rev (c)] +| 5: [rev ()] +| 5: =>() +| 4: =>(c) +| 3: =>(c b) +| 2: =>(c b a) +(c b a) +@end lisp + +@noindent +The number before the colon in this output (which follows @code{(ice-9 +debugging trace)}'s default output format) is the number of real frames +on the stack. The fact that this number increases for each recursive +call confirms that the implementation above of @code{rev} is not +tail-recursive. + +In the next example, we probe the @emph{internal} workings of +@code{rev} in more detail by using the @code{trace-until-exit} +behaviour. + +@lisp +guile> (uninstall-trap t1) +guile> (define t2 (make <procedure-trap> + #:procedure rev + #:behaviour (list trace-trap + trace-until-exit))) +guile> (install-trap t2) +guile> (rev '(a b)) +| 2: [rev (a b)] +| 2: (if (null? ls) ls (append (rev (cdr ls)) (list (car ls)))) +| 3: (null? ls) +| 3: [null? (a b)] +| 3: =>#f +| 2: (append (rev (cdr ls)) (list (car ls))) +| 3: (rev (cdr ls)) +| 4: (cdr ls) +| 4: [cdr (a b)] +| 4: =>(b) +| 3: [rev (b)] +| 3: (if (null? ls) ls (append (rev (cdr ls)) (list (car ls)))) +| 4: (null? ls) +| 4: [null? (b)] +| 4: =>#f +| 3: (append (rev (cdr ls)) (list (car ls))) +| 4: (rev (cdr ls)) +| 5: (cdr ls) +| 5: [cdr (b)] +| 5: =>() +| 4: [rev ()] +| 4: (if (null? ls) ls (append (rev (cdr ls)) (list (car ls)))) +| 5: (null? ls) +| 5: [null? ()] +| 5: =>#t +| 4: (list (car ls)) +| 5: (car ls) +| 5: [car (b)] +| 5: =>b +| 4: [list b] +| 4: =>(b) +| 3: [append () (b)] +| 3: =>(b) +| 3: (list (car ls)) +| 4: (car ls) +| 4: [car (a b)] +| 4: =>a +| 3: [list a] +| 3: =>(a) +| 2: [append (b) (a)] +| 2: =>(b a) +(b a) +@end lisp + +@noindent +The output in this case shows every step that the evaluator performs +in evaluating @code{(rev '(a b))}. + + +@node Tracing Configuration +@subsubsection Tracing Configuration + +The detail of what gets printed in each trace line, and the port to +which tracing is written, can be configured by the procedures +@code{set-trace-layout} and @code{trace-port}, both exported by the +@code{(ice-9 debugging trace)} module. + +@deffn {Procedure with Setter} trace-port +Get or set the port to which tracing is printed. The default is the +value of @code{(current-output-port)} when the @code{(ice-9 debugging +trace)} module is first loaded. +@end deffn + +@deffn {Procedure} set-trace-layout format-string . arg-procs +Layout each trace line using @var{format-string} and @var{arg-procs}. +For each trace line, the list of values to be printed is obtained by +calling all the @var{arg-procs}, passing the trap context as the only +parameter to each one. This list of values is then formatted using +the specified @var{format-string}. +@end deffn + +@noindent +The @code{(ice-9 debugging trace)} module exports a set of arg-proc +procedures to cover most common needs, with names beginning +@code{trace/}. These are all implemented on top of the @code{tc:} trap +context accessor procedures documented in @ref{Trap Context}, and if any +trace output not provided by the following is needed, it should be +possible to implement based on a combination of the @code{tc:} +procedures. + +@deffn {Procedure} trace/pid trap-context +An arg-proc that returns the current process ID. +@end deffn + +@deffn {Procedure} trace/stack-id trap-context +An arg-proc that returns the stack ID of the stack in which the +current trap occurred. +@end deffn + +@deffn {Procedure} trace/stack-depth trap-context +An arg-proc that returns the length (including non-real frames) of the +stack at the point of the current trap. +@end deffn + +@deffn {Procedure} trace/stack-real-depth trap-context +An arg-proc that returns the length excluding non-real frames of the +stack at the point of the current trap. +@end deffn + +@deffn {Procedure} trace/stack trap-context +An arg-proc that returns a string summarizing stack information. This +string includes the stack ID, real depth, and count of additional +non-real frames, with the format @code{"~a:~a+~a"}. +@end deffn + +@deffn {Procedure} trace/source-file-name trap-context +An arg-proc that returns the name of the source file for the innermost +stack frame, or an empty string if source is not available for the +innermost frame. +@end deffn + +@deffn {Procedure} trace/source-line trap-context +An arg-proc that returns the line number of the source code for the +innermost stack frame, or zero if source is not available for the +innermost frame. +@end deffn + +@deffn {Procedure} trace/source-column trap-context +An arg-proc that returns the column number of the start of the source +code for the innermost stack frame, or zero if source is not available +for the innermost frame. +@end deffn + +@deffn {Procedure} trace/source trap-context +An arg-proc that returns the source location for the innermost stack +frame. This is a string composed of file name, line and column number +with the format @code{"~a:~a:~a"}, or an empty string if source is not +available for the innermost frame. +@end deffn + +@deffn {Procedure} trace/type trap-context +An arg-proc that returns a three letter abbreviation indicating the +type of the current trap: @code{"APP"} for an application frame, +@code{"EVA"} for an evaluation, @code{"RET"} for an exit trap, or +@code{"ERR"} for an error (pseudo-)trap. +@end deffn + +@deffn {Procedure} trace/real? trap-context +An arg-proc that returns @code{" "} if the innermost stack frame is a +real frame, or @code{"t"} if it is not. +@end deffn + +@deffn {Procedure} trace/info trap-context +An arg-proc that returns a string describing the expression being +evaluated, application being performed, or return value, according to +the current trap type. +@end deffn + +@noindent +@code{trace/stack-depth} and @code{trace/stack-real-depth} are identical +to the trap context methods @code{tc:depth} and @code{tc:real-depth} +described before (@pxref{Trap Context}), but renamed here for +convenience. + +The default trace layout, as exhibited by the examples of the previous +subsubsubsection, is set by this line of code from the @code{(ice-9 debugging +traps)} module: + +@lisp +(set-trace-layout "|~3@@a: ~a\n" trace/stack-real-depth trace/info) +@end lisp + +@noindent +If we rerun the first of those examples, but with trace layout +configured to show source location and trap type in addition, the +output looks like this: + +@lisp +guile> (set-trace-layout "| ~25a ~3@@a: ~a ~a\n" + trace/source + trace/stack-real-depth + trace/type + trace/info) +guile> (rev '(a b c)) +| standard input:29:0 2: APP [rev (a b c)] +| standard input:4:21 3: APP [rev (b c)] +| standard input:4:21 4: APP [rev (c)] +| standard input:4:21 5: APP [rev ()] +| standard input:2:9 5: RET =>() +| standard input:4:13 4: RET =>(c) +| standard input:4:13 3: RET =>(c b) +| standard input:4:13 2: RET =>(c b a) +(c b a) +@end lisp + + +@node Tracing and (ice-9 debug) +@subsubsection Tracing and (ice-9 debug) + +The @code{(ice-9 debug)} module of the core Guile distribution +provides a tracing facility that is roughly similar to that described +here, but there are important differences. + +@itemize @bullet +@item +The @code{(ice-9 debug)} trace gives a nice pictorial view of changes +in stack depth, by using indentation like this: + +@lisp +[fact1 4] +| [fact1 3] +| | [fact1 2] +| | | [fact1 1] +| | | | [fact1 0] +| | | | 1 +| | | 1 +| | 2 +| 6 +24 +@end lisp + +However its output can @emph{only} show the information seen here, +which corresponds to @code{guile-debugging}'s @code{trace/info} +procedure; it cannot be configured to show other pieces of information +about the trap context in the way that @code{guile-debugging}'s trace +feature can. + +@item +The @code{(ice-9 debug)} trace only allows the tracing of procedure +applications and their return values, whereas @code{guile-debugging}'s +trace allows any kind of trap to be traced. + +It's interesting to note that @code{(ice-9 debug)}'s restriction here, +which might initially appear to be just a straightforward consequence +of its implementation, is also somewhat dictated by its pictorial +display. The use of indentation in the output relies on hooking into +the low level trap calls in such a way that the trapped application +entries and exits exactly balance each other. +@code{guile-debugging}'s more general traps interface allows traps to +be installed such that entry and exit traps don't necessarily balance, +which means that, in general, indentation diagrams like the one above +don't work. +@end itemize + +It isn't currently possible to use both @code{(ice-9 debug)} trace and +@code{guile-debugging} in the same Guile session, because their settings +of the low level trap options conflict with each other. (It should be +possible to fix this, by modifying @code{(ice-9 debug)} to use +@code{guile-debugging}'s trap installation interface, but only if and +when @code{guile-debugging} is integrated into the core Guile +distribution.) + + +@node Traps Installing More Traps +@subsubsection Traps Installing More Traps + +Sometimes it is desirable for the behaviour at one trap to install +further traps. In other words, the behaviour is something like +``Don't do much right now, but set things up to stop after two or +three more steps'', or ``@dots{} when this frame completes''. This is +absolutely fine. For example, it is easy to code a generic ``do +so-and-so when the current frame exits'' procedure, which can be used +wherever a trap context is available, as follows. + +@lisp +(define (at-exit trap-context behaviour) + (install-trap (make <exit-trap> + #:depth (tc:depth trap-context) + #:single-shot #t + #:behaviour behaviour))) +@end lisp + +To continue and pin down the example, this could then be used as part +of a behaviour whose purpose was to measure the accumulated time spent +in and below a specified procedure. + +@lisp +(define calls 0) +(define total 0) + +(define accumulate-time + (lambda (trap-context) + (set! calls (+ calls 1)) + (let ((entry (current-time))) + (at-exit trap-context + (lambda (ignored) + (set! total + (+ total (- (current-time) + entry)))))))) + +(install-trap (make <procedure-trap> + #:procedure my-proc + #:behaviour accumulate-time)) +@end lisp + + +@node Common Trap Options +@subsubsection Common Trap Options + +When creating any kind of trap object, settings for the trap being +created are specified as options on the @code{make} call using syntax +like this: + +@lisp +(make <@var{trap-class}> + #:@var{option-keyword} @var{setting} + @dots{}) +@end lisp + +The following common options are provided by the base class +@code{<trap>}, and so can be specified for any kind of trap. + +@deffn {Class} <trap> +Base class for trap objects. +@end deffn + +@deffn {Trap Option} #:condition thunk +If not @code{#f}, this is a thunk which is called when the trap fires, +to determine whether trap processing should proceed any further. If +the thunk returns @code{#f}, the trap is basically suppressed. +Otherwise processing continues normally. (Default value @code{#f}.) +@end deffn + +@deffn {Trap Option} #:skip-count count +A count of valid (after @code{#:condition} processing) firings of this +trap to skip. (Default value 0.) +@end deffn + +@deffn {Trap Option} #:single-shot boolean +If not @code{#f}, this indicates that the trap should be automatically +uninstalled after it has successfully fired (after @code{#:condition} +and @code{#:skip-count} processing) for the first time. (Default +value @code{#f}.) +@end deffn + +@deffn {Trap Option} #:behaviour behaviour-proc +A trap behaviour procedure --- as discussed in the preceding subsubsection +--- or a list of such procedures, in which case each procedure is +called in turn when the trap fires. (Default value @code{'()}.) +@end deffn + +@deffn {Trap Option} #:repeat-identical-behaviour boolean +Normally, if multiple trap objects are triggered by the same low level +trap, and they request the same behaviour, it's only actually useful +to do that behaviour once (per low level trap); so by default multiple +requests for the same behaviour are coalesced. If this option is set +other than @code{#f}, the contents of the @code{#:behaviour} option +are uniquified so that they avoid being coalesced in this way. +(Default value @code{#f}.) +@end deffn + + +@node Procedure Traps +@subsubsection Procedure Traps + +The @code{<procedure-trap>} class implements traps that are triggered +upon application of a specified procedure. Instances of this class +should use the @code{#:procedure} option to specify the procedure to +trap on. + +@deffn {Class} <procedure-trap> +Class for traps triggered by application of a specified procedure. +@end deffn + +@deffn {Trap Option} #:procedure procedure +Specifies the procedure to trap on. +@end deffn + +@noindent +Example: + +@lisp +(install-trap (make <procedure-trap> + #:procedure my-proc + #:behaviour (list trace-trap + trace-until-exit))) +@end lisp + + +@node Exit Traps +@subsubsection Exit Traps + +The @code{<exit-trap>} class implements traps that are triggered upon +stack frame exit past a specified stack depth. Instances of this +class should use the @code{#:depth} option to specify the target stack +depth. + +@deffn {Class} <exit-trap> +Class for traps triggered by exit past a specified stack depth. +@end deffn + +@deffn {Trap Option} #:depth depth +Specifies the reference depth for the trap. +@end deffn + +@noindent +Example: + +@lisp +(define (trace-at-exit trap-context) + (install-trap (make <exit-trap> + #:depth (tc:depth trap-context) + #:single-shot #t + #:behaviour trace-trap))) +@end lisp + +@noindent +(This is the actual definition of the @code{trace-at-exit} behaviour.) + + +@node Entry Traps +@subsubsection Entry Traps + +The @code{<entry-trap>} class implements traps that are triggered upon +any stack frame entry. No further parameters are needed to specify an +instance of this class, so there are no class-specific trap options. +Note that it remains possible to use the common trap options +(@pxref{Common Trap Options}), for example to set a trap for the +@var{n}th next frame entry. + +@deffn {Class} <entry-trap> +Class for traps triggered by any stack frame entry. +@end deffn + +@noindent +Example: + +@lisp +(install-trap (make <entry-trap> + #:skip-count 5 + #:behaviour gds-debug-trap)) +@end lisp + + +@node Apply Traps +@subsubsection Apply Traps + +The @code{<apply-trap>} class implements traps that are triggered upon +any procedure application. No further parameters are needed to +specify an instance of this class, so there are no class-specific trap +options. Note that it remains possible to use the common trap options +(@pxref{Common Trap Options}), for example to set a trap for the next +application where some condition is true. + +@deffn {Class} <apply-trap> +Class for traps triggered by any procedure application. +@end deffn + +@noindent +Example: + +@lisp +(install-trap (make <apply-trap> + #:condition my-condition + #:behaviour gds-debug-trap)) +@end lisp + + +@node Step Traps +@subsubsection Step Traps + +The @code{<step-trap>} class implements traps that do single-stepping +through a program's execution. They come in two flavours, with and +without a specified file name. If a file name is specified, the trap +is triggered by the next evaluation, application or frame exit +pertaining to source code from the specified file. If a file name is +not specified, the trap is triggered by the next evaluation, +application or frame exit from any file (or for code whose source +location was not recorded), in other words by the next evaluator step +of any kind. + +The design goal of the @code{<step-trap>} class is to match what a +user would intuitively think of as single-stepping through their code, +either through code in general (roughly corresponding to GDB's +@code{step} command, for example), or through code from a particular +source file (roughly corresponding to GDB's @code{next}). Therefore +if you are using @code{guile-debugging} to single-step through code +and finding its behaviour counter-intuitive, please let me know so +that I can improve it. + +The implementation and options of the @code{<step-trap>} class are +complicated by the fact that it is unreliable to determine whether a +low level frame exit trap is applicable to a specified file by +examining the details of the reported frame. This is a consequence of +tail recursion, which has the effect that many frames can be removed +from the stack at once, with only the outermost frame being reported +by the low level trap call. The effects of this on the +@code{<step-trap>} class are such as to require the introduction of +the strange-looking @code{#:exit-depth} option, for the following +reasons. + +@itemize @bullet +@item +When stopped at the start of an application or evaluation frame, and +it is desired to continue execution until the next ``step'' in the same +source file, that next step could be the start of a nested application +or evaluation frame, or --- if the procedure definition is in a +different file, for example --- it could be the exit from the current +frame. + +@item +Because of the effects of tail recursion noted above, the current +frame exit possibility must be expressed as frame exit past a +specified stack depth. When an instance of the @code{<step-trap>} +class is installed from the context of an application or evaluation +frame entry, the @code{#:exit-depth} option should be used to specify +this stack depth. + +@item +When stopped at a frame exit, on the other hand, we know that the next +step must be an application or evaluation frame entry. In this +context the @code{#:exit-depth} option is not needed and should be +omitted or set to @code{#f}. +@end itemize + +@noindent +When a step trap is installed without @code{#:single-shot #t}, such +that it keeps firing, the @code{<step-trap>} code automatically +updates its idea of the @code{#:exit-depth} setting each time, so that +the trap always fires correctly for the following step. + +@deffn {Class} <step-trap> +Class for single-stepping traps. +@end deffn + +@deffn {Trap Option} #:file-name name +If not @code{#f}, this is a string containing the name of a source +file, and restricts the step trap to evaluation steps within that +source file. (Default value @code{#f}.) +@end deffn + +@deffn {Trap Option} #:exit-depth depth +If not @code{#f}, this is a positive integer implying that the next +step may be frame exit past the stack depth @var{depth}. See the +discussion above for more details. (Default value @code{#f}.) +@end deffn + +@noindent +Example: + +@lisp +(install-trap (make <step-trap> + #:file-name (frame-file-name + (stack-ref stack index)) + #:exit-depth (- (stack-length stack) + (stack-ref stack index)) + #:single-shot #t + #:behaviour debug-trap)) +@end lisp + + +@node Source Traps +@subsubsection Source Traps + +The @code{<source-trap>} class implements traps that are attached to a +precise source code expression, as read by the reader, and which fire +each time that that expression is evaluated. These traps use a low +level Guile feature which can mark individual expressions for +trapping, and are relatively efficient. But it can be tricky to get +at the source expression in the first place, and these traps are +liable to become irrelevant if the procedure containing the expression +is reevaluated; these issues are discussed further below. + +@deffn {Class} <source-trap> +Class for traps triggered by evaluation of a specific Scheme +expression. +@end deffn + +@deffn {Trap Option} #:expression expr +Specifies the Scheme expression to trap on. +@end deffn + +@noindent +Example: + +@lisp +(display "Enter an expression: ") +(let ((x (read))) + (install-trap (make <source-trap> + #:expression x + #:behaviour (list trace-trap + trace-at-exit))) + (primitive-eval x)) +@print{} +Enter an expression: (+ 1 2 3 4 5 6) +| 3: (+ 1 2 3 4 5 6) +| 3: =>21 +21 +@end lisp + +The key point here is that the expression specified by the +@code{#:expression} option must be @emph{exactly} (i.e. @code{eq?} to) +what is going to be evaluated later. It doesn't work, for example, to +say @code{#:expression '(+ x 3)}, with the expectation that the trap +will fire whenever evaluating any expression @code{(+ x 3)}. + +The @code{trap-here} macro can be used in source code to create and +install a source trap correctly. Take for example the factorial +function defined in the @code{(ice-9 debugging example-fns)} module: + +@lisp +(define (fact1 n) + (if (= n 0) + 1 + (* n (fact1 (- n 1))))) +@end lisp + +@noindent +To set a source trap on a particular expression --- let's say the +expression @code{(= n 0)} --- edit the code so that the expression is +enclosed in a @code{trap-here} macro call like this: + +@lisp +(define (fact1 n) + (if (trap-here (= n 0) #:behaviour debug-trap) + 1 + (* n (fact1 (- n 1))))) +@end lisp + +@deffn {Macro} trap-here expression . trap-options +Install a source trap with options @var{trap-options} on +@var{expression}, then return with the whole call transformed to +@code{(begin @var{expression})}. +@end deffn + +Note that if the @code{trap-here} incantation is removed, and +@code{fact1} then redefined by reloading its source file, the effect +of the source trap is lost, because the text ``(= n 0)'' is read again +from scratch and becomes a new expression @code{(= n 0)} which does +not have the ``trap here'' mark on it. + +If the semantics and setting of source traps seem unwieldy, location +traps may meet your need more closely; these are described in the +following subsubsection. + + +@node Location Traps +@subsubsection Location Traps + +The @code{<location-trap>} class implements traps that are triggered +by evaluation of code at a specific source location or within a +specified range of source locations. When compared with source traps, +they are easier to set, and do not become irrelevant when the relevant +code is reloaded; but unfortunately they are considerably less +efficient, as they require running some ``are we in the right place +for a trap'' code on every low level frame entry trap call. + +@deffn {Class} <location-trap> +Class for traps triggered by evaluation of code at a specific source +location or in a specified range of source locations. +@end deffn + +@deffn {Trap Option} #:file-regexp regexp +A regular expression specifying the filenames that will match this +trap. This option must be specified when creating a location trap. +@end deffn + +@deffn {Trap Option} #:line line-spec +If specified, @var{line-spec} describes either a single line, in which +case it is a single integer, or a range of lines, in which case it is +a pair of the form @code{(@var{min-line} . @var{max-line})}. All line +numbers are 0-based, and the range form is inclusive-inclusive. If +@code{#f} or not specified, the trap is not restricted by line number. +(Default value @code{#f}.) +@end deffn + +@deffn {Trap Option} #:column column-spec +If specified, @var{column-spec} describes either a single column, in +which case it is a single integer, or a range of columns, in which +case it is a pair of the form @code{(@var{min-column} +. @var{max-column})}. All column numbers are 0-based, and the range +form is inclusive-inclusive. If @code{#f} or not specified, the trap +is not restricted by column number. (Default value @code{#f}.) +@end deffn + +@noindent +Example: + +@lisp +(install-trap (make <location-trap> + #:file-regexp "example-fns.scm" + #:line '(11 . 13) + #:behaviour gds-debug-trap)) +@end lisp + + +@node Trap Shorthands +@subsubsection Trap Shorthands + +If the code described in the preceding subsubsections for creating and +manipulating traps seems a little long-winded, it is of course +possible to define more convenient shorthand forms for typical usage +patterns. For example, my own @file{.guile} file contains the +following definitions for setting breakpoints and for tracing. + +@lisp +(define (break! proc) + (install-trap (make <procedure-trap> + #:procedure proc + #:behaviour gds-debug-trap))) + +(define (trace! proc) + (install-trap (make <procedure-trap> + #:procedure proc + #:behaviour (list trace-trap + trace-at-exit)))) + +(define (trace-subtree! proc) + (install-trap (make <procedure-trap> + #:procedure proc + #:behaviour (list trace-trap + trace-until-exit)))) +@end lisp + +Definitions like these are not provided out-of-the-box by +@code{guile-debugging}, because different users will have different +ideas about what their default debugger should be, or, for example, +which of the common trap options (@pxref{Common Trap Options}) it +might be useful to expose through such shorthand procedures. + + +@node Trap Utilities +@subsubsection Trap Utilities + +@code{list-traps} can be used to print a description of all known trap +objects. This uses a weak value hash table, keyed by a trap index +number. Each trap object has its index number assigned, and is added +to the hash table, when it is created by a @code{make @var{trap-class} +@dots{}} call. When a trap object is GC'd, it is automatically +removed from the hash table, and so no longer appears in the output +from @code{list-traps}. + +@deffn {Variable} all-traps +Weak value hash table containing all known trap objects. +@end deffn + +@deffn {Procedure} list-traps +Print a description of all known trap objects. +@end deffn + +The following example shows a single trap that traces applications of +the procedure @code{facti}. + +@lisp +guile> (list-traps) +#<<procedure-trap> 100d2e30> is an instance of class <procedure-trap> +Slots are: + number = 1 + installed = #t + condition = #f + skip-count = 0 + single-shot = #f + behaviour = (#<procedure trace-trap (trap-context)>) + repeat-identical-behaviour = #f + procedure = #<procedure facti (n a)> +@end lisp + +When @code{all-traps} or @code{list-traps} reveals a trap that you +want to modify but no longer have a reference to, you can retrieve the +trap object by calling @code{get-trap} with the trap's number. For +example, here's how you could change the behaviour of the trap listed +just above. + +@lisp +(slot-set! (get-trap 1) 'behaviour (list debug-trap)) +@end lisp + +@deffn {Procedure} get-trap number +Return the trap object with the specified @var{number}, or @code{#f} +if there isn't one. +@end deffn + + +@node Breakpoints +@subsection Breakpoints + +While they are an important piece of infrastructure, and directly +usable in some scenarios, traps are still too low level to meet some +of the requirements of interactive development. + +For example, in my experience a common scenario is that a newly +written procedure is not working properly, and so you'd like to be +able to step or trace through its code to find out why. Ideally this +should be possible from the IDE and without having to modify the +source code. There are two problems with using traps directly in this +scenario. + +@enumerate +@item +They are too detailed: constructing and installing a trap requires you +to say what kind of trap you want and to specify fairly low level +options for it, whereas what you really want is just to say ``break +here using the most efficient means possible.'' + +@item +The most efficient kinds of trap --- that is, @code{<procedure-trap>} +and @code{<source-trap>} --- can only be specified and installed +@emph{after} the code that they refer to has been loaded. This is an +inconvenient detail for the user to deal with, and in some +applications it might be very difficult to insert an instruction to +install the required trap in between when the code is loaded and when +the procedure concerned is first called. It would be better to be +able to tell Guile about the requirement upfront, and for it to deal +with installing the trap when possible. +@end enumerate + +We solve these problems by introducing breakpoints. A breakpoint is +something which says ``I want to break at location X, or in procedure +P --- just make it happen'', and can be set regardless of whether the +relevant code has already been loaded. Breakpoints use traps to do +their work, but that is a detail that the user will usually not have +to care about. + +Breakpoints are provided by a combination of Scheme code in the client +program, and facilities for setting and managing breakpoints in the +GDS front end. On the Scheme side the entry points are as follows. + +@deffn {Getter with Setter} default-breakpoint-behaviour +A ``getter with setter'' procedure that can be used to get or set the +default behaviour for new breakpoints. When a new default behaviour +is set, by calling + +@lisp +(set! (default-breakpoint-behaviour) @var{new-behaviour}) +@end lisp + +@noindent +the new behaviour applies to all following @code{break-in} and +@code{break-at} calls, but does not affect breakpoints which have +already been set. @var{new-behaviour} should be a behaviour procedure +with the signature + +@lisp +(lambda (trap-context) @dots{}) +@end lisp + +@noindent +as described in @ref{Specifying Trap Behaviour}. +@end deffn + +@deffn {Procedure} break-in procedure-name [module-or-file-name] [options] +Set a breakpoint on entry to the procedure named @var{procedure-name}, +which should be a symbol. @var{module-or-file-name}, if present, is +the name of the module (a list of symbols) or file (a string) which +includes the target procedure. If @var{module-or-file-name} is +absent, the target procedure is assumed to be in the current module. + +The available options are any of the common trap options +(@pxref{Common Trap Options}), and are used when creating the +breakpoint's underlying traps. The default breakpoint behaviour +(given earlier to @code{default-breakpoint-behaviour}) is only used if +these options do not include @code{#:behaviour @var{behaviour}}. +@end deffn + +@deffn {Procedure} break-at file-name line column [options] +Set a breakpoint on the expression in file @var{file-name} whose +opening parenthesis is on line @var{line} at column @var{column}. +@var{line} and @var{column} both count from 0 (not from 1). + +The available options are any of the common trap options +(@pxref{Common Trap Options}), and are used when creating the +breakpoint's underlying traps. The default breakpoint behaviour +(given earlier to @code{default-breakpoint-behaviour}) is only used if +these options do not include @code{#:behaviour @var{behaviour}}. +@end deffn + +@deffn {Procedure} set-gds-breakpoints +Ask the GDS front end for a list of breakpoints to set, and set these +using @code{break-in} and @code{break-at} as appropriate. +@end deffn + +@code{default-breakpoint-behaviour}, @code{break-in} and +@code{break-at} allow an application's startup code to specify any +breakpoints that it needs inline in that code. For example, to trace +calls and arguments to a group of procedures to handle HTTP requests, +one might write something like this: + +@lisp +(use-modules (ice-9 debugging breakpoints) + (ice-9 debugging trace)) + +(set! (default-breakpoint-behaviour) trace-trap) + +(break-in 'handle-http-request '(web http)) +(break-in 'read-http-request '(web http)) +(break-in 'decode-form-data '(web http)) +(break-in 'send-http-response '(web http)) +@end lisp + +@code{set-gds-breakpoints} can be used as well as or instead of the +above, and is intended to be the most practical option if you are +using GDS. The idea is that you only need to add this one call +somewhere in your application's startup code, like this: + +@lisp +(use-modules (ice-9 gds-client)) +(set-gds-breakpoints) +@end lisp + +@noindent +and then all the details of the breakpoints that you want to set can +be managed through GDS. For the details of GDS's breakpoints +interface, see @ref{Setting and Managing Breakpoints}. + + @c Local Variables: @c TeX-master: "guile.texi" @c End: diff --git a/doc/ref/scheme-using.texi b/doc/ref/scheme-using.texi index b596ce50a..f7f49a45d 100644 --- a/doc/ref/scheme-using.texi +++ b/doc/ref/scheme-using.texi @@ -309,11 +309,16 @@ however it may be continued over multiple lines. [to be completed] @deffn {Debugger Command} step [n] -Continue until entry to @var{n}th next frame. +Tell the debugged program to do @var{n} single-steps to the next frame +entry or exit of any kind. @var{n} defaults to 1. @end deffn @deffn {Debugger Command} next [n] -Continue until entry to @var{n}th next frame in same file. +Tell the debugged program to do @var{n} single-steps to the entry or +exit of a frame whose code comes from the same source file as the +selected stack frame. (See @ref{Step Traps} for the details of how +this works.) If the selected stack frame has no source, the effect of +this command is the same as of @code{step}. @var{n} defaults to 1. @end deffn @@ -323,8 +328,9 @@ Continue until entry to @var{n}th next frame in same file. [to be completed] @deffn {Debugger Command} finish -Continue until evaluation of the current frame is complete, and -print the result obtained. +Tell the program being debugged to continue running until the +completion of the selected stack frame, and at that time to print the +result and reenter the command line debugger. @end deffn @deffn {Debugger Command} trace-finish @@ -338,7 +344,7 @@ Trace until evaluation of the current frame is complete. [to be completed] @deffn {Debugger Command} continue -Continue program execution. +Tell the program being debugged to continue running. @end deffn @@ -405,7 +411,774 @@ can also run a program until it hits a breakpoint, then examine, modify and reevaluate some of the relevant code, and then tell the program to continue running. +@ignore +GDS is a user interface for working on Guile Scheme programs in Emacs. +It aims to provide whatever facilities are needed to make the writing, +debugging and maintenance of Scheme code in Emacs as fluid and +productive as possible. These facilities currently include the +following. + +@table @asis +@item Displaying the Scheme stack +When running Scheme code hits a trap or throws an exception, GDS can +display the stack at the point of the trap or exception. The +presentation makes it very easy to move up and down the stack, showing +whenever possible the source code for each frame in another Emacs +buffer, and allowing you to evaluate test expressions in the context of +the selected frame. + +@item Continuing execution from a trap +When GDS is showing the stack for code that has hit a trap, it also +allows you to control how execution continues from that point. For +example you can select a stack frame and tell Guile to run until that +frame completes, at which point GDS will display the frame's return +value. + +@item Evaluating Scheme code +GDS allows you to select a region of a Scheme buffer and send it to +Guile for evaluation, or to enter a Scheme expression to be evaluated in +the Emacs minibuffer. In both cases the evaluation results are popped +up in a temporary Emacs window. + +@item Setting breakpoints in Scheme code +GDS makes it easy to set breakpoints in Scheme code from within Emacs. +Deep down this uses the traps described in previous chapters, but GDS +makes the practicalities as simple as typing @kbd{C-x @key{SPC}}. When +a GDS breakpoint is hit, the stack at that point is popped up in Emacs. +GDS also remembers your breakpoints between editing sessions, so you +don't have to set them again when you visit the relevant files. + +@item Access to Guile's built in help system +GDS makes it easy to query Guile's ``help'' and ``apropos'' commands, +and pops up the results in a temporary Emacs window. + +@item Symbol completion +GDS provides a keystroke which tries to complete a partially entered +symbol by asking Guile to match it against all the accessible bindings. +@end table + +(For a hands-on, tutorial introduction to using GDS, use Emacs to open +the file @file{gds-tutorial.txt}, which is included with the +guile-debugging distribution, and then follow the steps in that file.) + +GDS can provide these facilities for any number of Guile Scheme programs +(which we often call ``clients'') at once, and these programs can be +started either completely independently of GDS, including outside Emacs, +or specifically @emph{by} GDS. The two common cases are: +@itemize +@item +a Guile application, such as @uref{http://www.gnucash.org, GnuCash}, +which is started from your desktop, and which connects to GDS as a +result of some incantation added to its startup code + +@item +a ``utility'' Guile process which is run by GDS to provide help, +completion and evaluation for Scheme code that you are working on in +Emacs. +@end itemize + +@noindent +The user experience --- in other words the ways that the GDS front end +allows you to interact with the client --- is much the same in all +cases. + +Communication between the Guile client program and GDS uses a TCP +socket, which means that it is orthogonal to any other interfaces that +the client program has. In particular GDS does not interfere with a +program's standard input and output. +@end ignore + +@menu +* GDS Setup:: +* How To Use GDS:: +* Displaying the Scheme Stack:: +* Continuing Execution:: +* Evaluating Scheme Code:: +* Setting and Managing Breakpoints:: +* Access to Guile Help and Completion:: +* Associating Buffers with Clients:: +* An Example GDS Session:: +* GDS Architecture:: +@end menu + + +@node GDS Setup +@subsection GDS Setup + +GDS's Scheme and Emacs Lisp files will have been installed in +the correct places system-wide when the @code{guile-debugging} package +as a whole was installed. To enable the use of GDS in your own Emacs +sessions, simply add + +@lisp +(require 'gds) +@end lisp + +@noindent +somewhere in your @file{.emacs} file. + + +@node How To Use GDS +@subsection How To Use GDS + +There are lots of ways to use GDS, but they boil down to two overall +approaches. + +@enumerate +@item +When you are writing Scheme code in Emacs, you can use GDS while you are +writing to help with things like name completion, looking up help, and +evaluating fragments of code to check that they do what you expect. + +The first time you do something that needs a running Guile process, GDS +will automatically create one as an Emacs subprocess. This Guile +program does nothing but wait for and act on instructions from GDS, and +we refer to it as a @dfn{utility} Guile client. + +Over time this utility Guile will accumulate the code that you ask it to +evaluate, and you can also tell it to load complete files or modules by +sending it @code{load} or @code{use-modules} expressions. You can set +breakpoints and evaluate code which hits those breakpoints, and GDS will +pop up the stack at the breakpoint so you can explore your code by +single-stepping and evaluating test expressions. + +@item +Alternatively, you can use GDS to explore and debug a Guile program or +script which is started independently of GDS. This could be a script +that you invoke from the command line, or a graphical Guile-using +application which is launched from your desktop's start menu. + +In this case the program has to put something in its startup code to +cause it to connect to GDS at some point: either immediately during the +startup processing, or later when an error occurs or a trap is hit. +Several possibilities for this are described below. + +Under certain conditions, then, the program will stop, pass its current +Scheme stack to GDS, and then wait for instruction before continuing +execution. At such points you can use GDS to explore the stack, +obviously, but also to set or delete other breakpoints, modify the +program's code (by editing and then reevaluating it from Emacs), and use +the help and completion facilities, before eventually telling the +program to single-step or to continue running normally. +@end enumerate + +Here are some of the ways that a Guile program or script can arrange in +its startup code to use GDS. + +@subsubsection Invoking GDS when an Exception Occurs + +@lisp +(use-modules (ice-9 gds-client) + (ice-9 debugging traps)) + +(on-lazy-handler-dispatch gds-debug-trap) +@end lisp + +This means that the program will use GDS to display the stack whenever +it hits an exception that is protected by a @code{lazy-catch} using +Guile's standard @code{lazy-catch-handler} (defined in +@file{boot-9.scm}). + +@code{lazy-catch-handler} is used by the @code{stack-catch} procedure, +provided by the @code{(ice-9 stack-catch)} module, so this will include +exceptions within a @code{stack-catch}. @code{lazy-catch-handler} is +also used by the standard Guile REPL, when you run Guile interactively, +so you can add the above lines to your @file{.guile} file if you want to +use GDS whenever something that you type into the REPL throws an +exception. + +@subsubsection Setting GDS-managed Breakpoints + +@lisp +(use-modules (ice-9 gds-client)) +(set-gds-breakpoints) +@end lisp + +These lines tell the program to connect to GDS immediately and download +a set of breakpoint definitions. The program sets those breakpoints in +its code, then continues running. + +When the program later hits one of the breakpoints, it will use GDS to +display the stack and wait for instruction on what to do next, as +described above. + +@subsubsection Setting Specific Breakpoints + +@lisp +(use-modules (ice-9 debugging breakpoints) + (ice-9 gds-client)) + +(break-in 'fact2 "ice-9/debugging/example-fns" + #:behaviour gds-debug-trap) +@end lisp + +In this example, the program chooses to define its breakpoint explicitly +in its code, rather than downloading definitions from GDS, but it still +uses GDS to control what happens when the breakpoint is hit, by +specifying @code{gds-debug-trap} as the breakpoint behaviour. + +@subsubsection Accepting GDS Instructions at Any Time + +In addition to setting breakpoints and/or an exception handler as +described above, a Guile program can in principle set itself up to +accept new instructions from GDS at any time, not just when it has +stopped at a breakpoint or exception. This would allow the GDS user to +set new breakpoints or to evaluate code in the context of the running +program, without having to wait for the program to stop first. + +@lisp +(use-modules (ice-9 gds-client)) +(gds-accept-input #t) +@end lisp + +@code{gds-accept-input} causes the calling program to loop processing +instructions from GDS, until GDS sends the @code{continue} instruction. +This blocks the thread that calls it, however, so it will normally be +more practical for the program to set up a dedicated GDS thread and call +@code{gds-accept-input} from that thread. + +For @code{select}-driven applications, an alternative approach would be +for the GDS client code to provide an API which allowed the application +to + +@itemize +@item +discover the file descriptors (or Scheme ports) that are used for +receiving instruction from the GDS front end, so that it could include +these in its @code{select} call + +@item +call the GDS instruction handler when @code{select} indicated data +available for reading on those descriptors/ports. +@end itemize + +@noindent +This approach is not yet implemented, though. + +@subsubsection Utility Guile Implementation + +We bring this subsection full circle by noting that the ``utility'' Guile +client, which GDS starts automatically when you use GDS as described +under approach 1 above, is really just a special case of ``a Guile +program or script which is started independently'' (approach 2), and +provides the services that the GDS front end needs by a simple +combination of some of the code fragments just described. + +To be precise, the code for the utility Guile client is essentially +this: + +@lisp +(use-modules (ice-9 gds-client)) + +(set-gds-breakpoints) +(named-module-use! '(guile-user) '(ice-9 session)) +(gds-accept-input #f)) +@end lisp + +@code{set-gds-breakpoints} works as already described. The +@code{named-module-use!} line ensures that the client can process +@code{help} and @code{apropos} expressions, which is what the front end +sends to implement lookups in Guile's online help. The @code{#f} +parameter to @code{gds-accept-input} means that the @code{continue} +instruction will not cause the instruction loop to exit, which makes +sense here because the utility client has nothing to do except to +process GDS instructions. + +(The utility client does not use @code{on-lazy-handler-dispatch}, +because it has its own mechanism for catching and reporting exceptions +in the code that it is asked to evaluate. This mechanism summarizes the +exception and gives the user a button they can click to see the full +stack, so the end result is very similar to what +@code{on-lazy-handler-dispatch} provides.) + + +@node Displaying the Scheme Stack +@subsection Displaying the Scheme Stack + +When you specify @code{gds-debug-trap} as the behaviour for a trap or +a breakpoint and the Guile program concerned hits that trap or +breakpoint, GDS displays the stack and the relevant Scheme source code +in Emacs, allowing you to explore the state of the program and then +decide what to do next. The same applies if the program calls +@code{(on-lazy-handler-dispatch gds-debug-trap)} and then throws an +exception that passes through @code{lazy-handler-dispatch}, except +that in this case you can only explore; it isn't possible to continue +normal execution after an exception. + +The following commands are available in the stack buffer for exploring +the state of the program. + +@table @asis +@item @kbd{u}, @kbd{C-p}, @kbd{@key{up}} +@findex gds-up +Select the stack frame one up from the currently selected frame +(@code{gds-up}). GDS displays stack frames with the innermost at the +top, so moving ``up'' means selecting a more ``inner'' frame. + +@item @kbd{d}, @kbd{C-n}, @kbd{@key{down}} +@findex gds-down +Select the stack frame one down from the currently selected frame +(@code{gds-down}). GDS displays stack frames with the innermost at the +top, so moving ``down'' means selecting a more ``outer'' frame. + +@item @kbd{@key{RET}} +@findex gds-select-stack-frame +Select the stack frame at point (@code{gds-select-stack-frame}). This +is useful after clicking somewhere in the stack trace with the mouse. +@end table + +Selecting a frame means that GDS will display the source code +corresponding to that frame in the adjacent window, and that +subsequent frame-sensitive commands, such as @code{gds-evaluate} (see +below) and @code{gds-step-over} (@pxref{Continuing Execution}), will +refer to that frame. + +@table @kbd +@item e +@findex gds-evaluate +Evaluate a variable or expression in the local environment of the +selected stack frame (@code{gds-evaluate}). The result is displayed in +the echo area. + +@item I +@findex gds-frame-info +Show summary information about the selected stack frame +(@code{gds-frame-info}). This includes what type of frame it is, the +associated expression, and the frame's source location, if any. + +@item A +@findex gds-frame-args +For an application frame, display the frame's arguments +(@code{gds-frame-args}). + +@item S +@findex gds-proc-source +For an application frame, show the Scheme source code of the procedure +being called (@code{gds-proc-source}). The source code (where +available) is displayed in the echo area. +@end table + +@kbd{S} (@code{gds-proc-source}) is useful when the procedure being +called was created by an anonymous @code{(lambda @dots{})} expression. +Such procedures appear in the stack trace as @code{<procedure #f +(@dots{})>}, which doesn't give you much clue as to what will happen +next. @kbd{S} will show you the procedure's code, which is usually +enough for you to identify it. + + +@node Continuing Execution +@subsection Continuing Execution + +If it makes sense to continue execution from the stack which is being +displayed, GDS provides the following further commands in the stack +buffer. + +@table @asis +@item @kbd{g}, @kbd{c}, @kbd{q} +@findex gds-go +Tell the program to continue running (@code{gds-go}). It may of course +stop again if it hits another trap, or another occurrence of the same +trap. + +The multiple keystrokes reflect that you can think of this as ``going'', +``continuing'' or ``quitting'' (in the sense of quitting the GDS +display). + +@item @kbd{@key{SPC}} +@findex gds-step-file +Tell the program to do a single-step to the next entry or exit of a +frame whose code comes from the same source file as the selected stack +frame (@code{gds-step-file}). + +In other words, you can hit @kbd{@key{SPC}} repeatedly to step through +the code in a given file, automatically stepping @emph{over} any +evaluations or procedure calls that use code from other files (or from +no file). + +If the selected stack frame has no source, the effect of this command is +the same as that of @kbd{i}, described next. + +@item @kbd{i} +@findex gds-step-into +Tell the debugged program to do a single-step to the next frame entry or +exit of any kind (@code{gds-step-into}). @kbd{i} therefore steps +through code at the most detailed level possible. + +@item @kbd{o} +@findex gds-step-over +Tell the debugged program to continue running until the selected stack +frame completes, and then to display its result (@code{gds-step-over}). +Note that the program may stop before then if it hits another trap; in +this case the trap telling it to stop when the marked frame completes +remains in place and so will still fire at the appropriate point. +@end table + + +@node Evaluating Scheme Code +@subsection Evaluating Scheme Code + +The following keystrokes and commands provide various ways of sending +code to a Guile client process for evaluation. + +@table @kbd +@item M-C-x +@findex gds-eval-defun +Evaluate the ``top level defun'' that the cursor is in, in other words +the smallest balanced expression which includes the cursor and whose +opening parenthesis is in column 0 (@code{gds-eval-defun}). + +@item C-x C-e +@findex gds-eval-last-sexp +Evaluate the expression that ends just before the cursor +(@code{gds-eval-last-sexp}). This is designed so that it is easy to +evaluate an expression that you have just finished typing. + +@item C-c C-e +@findex gds-eval-expression +Read a Scheme expression using the minibuffer, and evaluate that +expression (@code{gds-eval-expression}). + +@item C-c C-r +@findex gds-eval-region +Evaluate the Scheme code in the marked region of the current buffer +(@code{gds-eval-region}). Note that GDS does not check whether the +region contains a balanced expression, or try to expand the region so +that it does; it uses the region exactly as it is. +@end table + + +@node Setting and Managing Breakpoints +@subsection Setting and Managing Breakpoints + +You can create a breakpoint in GDS by typing @kbd{C-x @key{SPC}} in a +Scheme mode buffer. To create a breakpoint on calls to a procedure +--- i.e. the equivalent of calling @code{break-in} --- place the +cursor on the procedure's name and type @kbd{C-x @key{SPC}}. To +create breakpoints on a particular expression, or on the series of +expressions in a particular region --- i.e. as with @code{break-at} +--- select the expression or region in the usual way and type @kbd{C-x +@key{SPC}}. In general, GDS assumes that you want a @code{break-at} +breakpoint if there is an active region, and a @code{break-in} +breakpoint otherwise. + +When you create a breakpoint like this, two things happen. Firstly, +if the current buffer is associated with a Guile client program, the +new breakpoint definition is immediately sent to that client (or, if +the client cannot accept input immediately, it is held in readiness to +pass to the client at the next possible opportunity). This allows the +new breakpoint to take effect as soon as possible in the relevant +client program. + +Secondly, it is added to GDS's @emph{global} list of all breakpoints. +This list holds the breakpoint information that will be given to any +client program that asks for it by calling @code{set-gds-breakpoints}. +The fact that this list is global, rather than client-specific, means +that the breakpoints you have set will automatically be recreated if +the program you are debugging has to be stopped and restarted --- +which in my experience happens often.@footnote{An important point here +is that there is nothing that unambiguously relates two subsequent +runs of the same client program, which might allow GDS to pass on +breakpoint settings more precisely.} + +(The only possible downside of this last point is that if you are +debugging two programs in parallel, which have some code in common, +you might not want a common code breakpoint in one program to be set +in the other program as well. But this feels like a small concern in +comparison to the benefit of breakpoints persisting as just described.) + + +@node Access to Guile Help and Completion +@subsection Access to Guile Help and Completion + +The following keystrokes provide fast and convenient access to Guile's +built in help, and to completion with respect to the set of defined and +accessible symbols. + +@table @kbd +@item C-h g +@findex gds-help-symbol +Get Guile help for a particular symbol, with the same results as if +you had typed @code{(help SYMBOL)} into the Guile REPL +(@code{gds-help-symbol}). The symbol to query defaults to the word at +or before the cursor but can also be entered or edited in the +minibuffer. The available help is popped up in a temporary Emacs +window. + +@item C-h C-g +@findex gds-apropos +List all accessible Guile symbols matching a given regular expression, +with the same results as if you had typed @code{(apropos REGEXP)} into +the Guile REPL (@code{gds-apropos}). The regexp to query defaults to +the word at or before the cursor but can also be entered or edited in +the minibuffer. The list of matching symbols is popped up in a +temporary Emacs window. + +@item M-@key{TAB} +@findex gds-complete-symbol +Try to complete the symbol at the cursor by matching it against the +set of all defined and accessible bindings in the associated Guile +process (@code{gds-complete-symbol}). If there are any extra +characters that can be definitively added to the symbol at point, they +are inserted. Otherwise, if there are any completions available, they +are popped up in a temporary Emacs window, where one of them can be +selected using either @kbd{@key{RET}} or the mouse. +@end table + + +@node Associating Buffers with Clients +@subsection Associating Buffers with Clients + +The first time that you use one of GDS's evaluation, help or completion +commands from a given Scheme mode buffer, GDS will ask which Guile +client program you want to use for the operation, or if you want to +start up a new ``utility'' client. After that GDS considers the buffer +to be ``associated'' with the selected client, and so sends all further +requests to that client, but you can override this by explicitly +associating the buffer with a different client, or by removing the +default association. + +@table @kbd +@item M-x gds-associate-buffer +Associate (or re-associate) the current buffer with a particular Guile +client program. The available clients are listed, and you can also +choose to start up a new ``utility'' client for this buffer to associate +with. + +@item M-x gds-dissociate-buffer +Dissociate the current buffer from its client, if any. This means that +the next time you use an evaluation, help or completion command, GDS +will ask you again which client to send the request to. +@end table + +When a buffer is associated with a client program, the buffer's modeline +shows whether the client is currently able to accept instruction from +GDS. This is done by adding one of the following suffixes to the +``Scheme'' major mode indicator: + +@table @asis +@item :ready +The client program (or one of its threads, if multithreaded) is +currently ready to accept instruction from GDS. In other words, if you +send it a help or evaluation request, you should see the result pretty +much immediately. + +@item :running +The client program is not currently able to accept instruction from +GDS. This means that it (or all of its threads, if multithreaded) is +busy, or waiting for input other than from GDS. + +@item :debug +The client program (or one of its threads, if multithreaded) is stopped +in ``debugging mode'' with GDS displaying the stack for a trap or +exception. It is waiting for instruction from GDS on what to do next. +@end table + + +@node An Example GDS Session +@subsection An Example GDS Session + +Create a file, @file{testgds.scm} say, for experimenting with GDS and +Scheme code, and type this into it: + +@lisp +(use-modules (ice-9 debugging traps) + (ice-9 gds-client) + (ice-9 debugging example-fns)) +(install-trap (make <procedure-trap> + #:behaviour gds-debug-trap + #:procedure fact1)) +@end lisp + +@noindent +Now select all of this code and type @kbd{C-c C-r} to send the selected +region to Guile for evaluation. GDS will ask you which Guile process to +use; unless you know that you already have another Guile application +running and connected to GDS, choose the ``Start a new Guile'' option, +which starts one of the ``utility'' processes described in @ref{How To +Use GDS}. + +The results of the evaluation pop up in a window like this: + +@lisp +(use-modules (ice-9 debugging traps)\n @dots{} + +;;; Evaluating subexpression 1 in current module (guile-user) + @result{} no (or unspecified) value + +;;; Evaluating subexpression 2 in current module (guile-user) + @result{} no (or unspecified) value + +--:** *Guile Evaluation* (Scheme:ready)--All------------ +@end lisp + +@noindent +this tells you that the evaluation was successful but that the return +values were unspecified. Its effect was to load a module of example +functions and set a trap on one of these functions, @code{fact1}, that +calculates the factorial of its argument. + +If you now call @code{fact1}, you can see the trap and GDS's stack +display in action. To do this add + +@lisp +(fact1 4) +@end lisp + +@noindent +to your @file{testgds.scm} buffer and type @kbd{C-x C-e} (which +evaluates the expression that the cursor is just after the end of). The +result is: + +@lisp +(fact1 4) + +;;; Evaluating in current module (guile-user) + @result{} 24 + +--:** *Guile Evaluation* (Scheme:ready)--All------------ +@end lisp + +@noindent +which is correct, but indicates that we forgot the step needed to enable +the trap mechanism. To do this, type @kbd{C-c C-e} and then enter + +@lisp +(trap-enable 'traps) +@end lisp + +@noindent +into the minibuffer. (You could equally have typed this into your test +file and evaluated it from there; we use @kbd{C-c C-e} here to +demonstrate the minibuffer option and because you typically wouldn't +want to leave this kind of global setting in the source code that you +are working on.) + +If you now type @kbd{C-x C-e} to evaluate @code{(fact1 4)} again, a GDS +stack window like the following appears: + +@lisp +Calling procedure: +=> s [fact1 4] + s [primitive-eval (fact1 4)] + + +--:** PID 28729 (Guile-Debug)--All------------ +@end lisp + +GDS's most compelling feature is its single-stepping. To get an +immediate feel for what this is like, make sure your Emacs is prepared +as described in @ref{GDS Setup}, then type the following code into an +interactive Guile session. + +@lisp +(fact1 4) +@end lisp + +@noindent +This will cause the GDS Guile-Debug window to pop up in Emacs, where +you can then press @kbd{i} once and @kbd{@key{SPC}} repeatedly to +single-step through the code from the point of the initial trap. + +(@kbd{i} is needed as the first keystroke rather than @kbd{@key{SPC}}, +because the aim here is to step through code in the @code{(ice-9 +debugging example-fns)} module, whose source file is +@file{@dots{}/ice-9/debugging/example-fns.scm}, but the initial +@code{(fact1 4)} call comes from the Guile session, whose ``source +file'' Guile presents as @file{standard input}. If the user starts by +pressing @kbd{@key{SPC}} instead of @kbd{i}, the effect is that the +program runs until it hits the first recursive call @code{(fact1 (- n +1))}, where it stops because of the trap on @code{fact1} firing again. +At this point, the source file @emph{is} +@file{@dots{}/ice-9/debugging/example-fns.scm}, because the recursive +@code{(fact1 (- n 1))} call comes from code in that file, so further +pressing of @kbd{@key{SPC}} successfully single-steps through this +file.) + + +@node GDS Architecture +@subsection GDS Architecture + +Ths following information may be of interest to readers who would like +to know how GDS works. Please note that understanding the details of +this subsection is completely optional so far as just using GDS is +concerned! + +GDS consists of three components. + +@itemize +@item +The GDS @dfn{interface} code is written in Emacs Lisp and runs inside +Emacs. This code, consisting of the installed files @file{gds.el} and +@file{gds-server.el}, is responsible for displaying information from +Guile in Emacs windows, and for responding to Emacs commands and +keystrokes by sending instructions back to the Guile program being +debugged. + +@item +The GDS @dfn{server} code is written in Scheme and runs as an Emacs +inferior process. It acts as a multiplexer between the (possibly +multiple) Guile programs being debugged and the interface code running +in Emacs. The server code is the installed file +@file{gds-server.scm}. + +@item +The GDS @dfn{client} code is written in Scheme (installed file +@file{gds-client.scm}), and is loaded as a module by each Guile +program that wants to use GDS for debugging. When a trap occurs whose +behaviour is @code{gds-debug-trap}, it feeds information about the +trap context through the server to Emacs, then waits for instruction +back from the Emacs interface on what to do next. +@end itemize + +@noindent +Summarized in glorious ASCII art, this looks as follows. + +@example ++------------+ +| Program #1 | +| | +| +--------+ | +| | Client |-_ +| +--------+ |-_ +---------------+ ++------------+ -_TCP | Emacs | + -_ | | + -_+--------+ | +-----------+ | + _| Server |-----| Interface | | ++------------+ _- +--------+ | +-----------+ | +| Program #2 | _- +---------------+ +| | _- TCP +| +--------+ _- +| | Client |-| +| +--------+ | ++------------+ +@end example + +@noindent +@cindex TCP, use of +The communication between the client and server components is over a +TCP connection, which has two implications. Firstly, that GDS is +independent of whatever other interfaces the programs being debugged +have, whether graphical or through standard input and output. +Secondly, that the server and Emacs interface can be on a different +computer from the programs being debugged (only theoretically, though, +because GDS doesn't yet provide an interface to connect to any server +other than the default, on localhost at TCP port 8333). The data +exchanged between client and server components, and between server and +interface components, is in the form of sexps that are organized so as +to be directly readable by both Scheme and Emacs Lisp. + + +@subsubsection Security Note + +@cindex Security +GDS currently has no authentication between its client and server +components, so in an untrusted environment the use of TCP probably +raises important security issues. If you are thinking of using GDS in +such an environment, please consider any such issues carefully before +proceeding! + + @c Local Variables: @c TeX-master: "guile.texi" @c End: |