guile-devel
[Top][All Lists]
Advanced

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

Re: Debugging plans


From: Neil Jerram
Subject: Re: Debugging plans
Date: 07 Jul 2001 13:23:05 +0100
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/20.7

>>>>> "Rob" == Rob Browning <address@hidden> writes:

    Rob> Just make sure we preserve the ability to fire a "hook" on
    Rob> every function call.

Short answer: OK.

Long answer: sounds like a cue for me to describe how I see Guile's
traps evolving (in 1.7.x) ...

Currently we have three trap handlers:

- the enter-frame handler is called when the evaluator enters a new
  frame, if (trap-enabled 'enter-frame) [i.e. unconditionally] or if
  ((debug-enabled 'breakpoints) and (frame source code is marked for a
  breakpoint) [this is SRCBRKP in C])

- the apply-frame handler is called when the evaluator is about to
  perform an application, if (trap-enabled 'apply-frame)
  [unconditional] or if ((debug-enabled 'trace) and (procedure is
  marked for tracing) [PROCTRACEP])

- the exit-frame handler is called when the evaluator exits a frame,
  if (trap-enabled 'exit-frame) [unconditional] or if ((debug-enabled
  'trace) and (procedure is marked for tracing) [PROCTRACEP]).

Observations...

- Frame entering and exiting is closely related to source code and its
  lexical structure.  If we had an IDE that permitted positioning the
  cursor and saying "set a breakpoint HERE", it would be natural to
  implement the breakpoint using SRCBRKP and an enter-frame handler
  proc.

  But it's not just source breakpoints that are related to source
  code.  The debugger `next' command is related to source code; and
  you could also do source-based tracing and profiling.

  In general, then, the enter- and exit-frame handlers provide
  source-based traps, and SRCBRKP should be interpreted as a way of
  flagging a point in the source to fire a source-based trap in
  general, not necessarily just a breakpoint.

- Inbetween entering and exiting a frame, you have the various kinds
  of action that the interpreter does.  Right now we only have the
  apply-frame handler, for applications, but conceivably we could add
  variable-lookup, macro-expansion, syntax-expansion,
  byte-code-execution, compiled-code-execution, autoload-module etc.
  In theory, all of these are places where it could be useful to trap.

- The apply-frame handler provides procedure-based traps.  PROCTRACEP
  is currently interpreted as flagging a procedure for tracing, but it
  could equally well be interpreted as flagging it for a breakpoint.
  (So that you could say `(break info)' to set a breakpoint (to enter
  the debugger) when about to perform an application using the
  procedure `info'.)  So it should be; note the parallelism here with
  reinterpretation of SRCBRKP for the enter-frame handler.

- Unconditional vs. flagged traps.  For each kind of trap, two types
  of usage seem to be useful; namely (i) call the trap handler for
  this trap every time (unconditionally), (ii) call the trap handler
  for this trap only if the associated object (source code, procedure,
  byte code object ...) has been flagged for it.

- Hooks vs. handlers.  (Although it was me that added them,) I don't
  like the use of a single, global handler procedure for each of the
  trap types.  It makes it difficult for multiple features (trace,
  breakpoints, profiling ...)  to install the handlers that they need
  without treading on each others' toes.  I think hooks would work
  much better.

- Continuations.  When about to call a trap handler, the evaluator
  creates a continuation or (if (debug-enabled 'cheaptraps)) a
  debug-object, and passes it to the handler proc.  There are three
  reasons for this, but all weak ones, and I think it would be
  preferable not to build a continuation/debug-object before calling
  the handler:

  (1) The continuation/debug-object can be used by the handler to
      create a stack for the trap point.  But this isn't necessary, as
      the handler can equally well do `(make-stack #t handler)'.

  (2) The continuation can be invoked to return a substitute
      expression for evaluation.  This could be achieved more cheaply
      by simply returning or throwing an exception with the substitute
      value.

  (3) The continuation could be saved and invoked (multiple times)
      later, to allow experimental debugging!  True, but the handler
      proc can use `call/cc' itself to achieve this; no need for the C
      code to set up the continuation in advance.

  Given that continuations are expensive, trap handlers will be less
  intrusive and more useful if we don't create a continuation for
  every trap handler call.

Putting together the points about flagging, hooks and continuations, I
see trap hooks being invoked by the evaluator like this:

  {
    scm_t_trap_data trap_data;
    trap_data.hook = scm_sys_application_trap_hook;
    trap_data.args = scm_list_2 (SCM_BOOL (PROCTRACEP (proc)), tailp);

    SCM_TRAPS_P = 0;
    substitute = scm_internal_catch (scm_sym_trap_return,
                                     scm_i_trap_dispatch,
                                     &trap_data,
                                     scm_i_trap_catch_handler,
                                     NULL);
    SCM_TRAPS_P = 1;

    if (!SCM_UNBNDP (substitute))
      {
        t.arg1 = substitute;   // or appropriate evaluator
        goto dispatch;         // incantation...
      }
  }

SCM scm_i_trap_dispatch (void *data)
{
  scm_t_trap_data *trap_data = (scm_t_trap_data *) data;
  scm_c_run_hook (trap_data->hook, trap_data->args);
  return SCM_UNDEFINED;
}

SCM scm_i_trap_catch_handler (void *data, SCM tag, SCM throw_args)
{
  return SCM_NULLP (throw_args) ? SCM_UNDEFINED : SCM_CAR (throw_args);
}

A few notes ...

- The above code runs, regardless of PROCTRACEP (proc), whenever
  scm_sys_application_trap_hook is not empty.  It's up to the hook
  procedures to decide whether or not to take notice of the `flagged?'
  indication that they get as theire first parameter.

- A hook procedure can return a substitute expression by `(throw
  'trap-return EXPR)'.

- The debugger can implement `continue' by `(throw 'trap-return)'.

That's plenty for now :-); thoughts anyone?


    Rob>                       I'm using that right now, along with
    Rob> setitimer and times to write a first pass at a statistical
    Rob> profiler.  For now there'll be no call graph or other fancy
    Rob> bits, but so far it's going pretty well.

This sounds very useful - good luck!

        Neil




reply via email to

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