[Top][All Lists]

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

Re: What debugger breakpoint features would you like?

From: Neil Jerram
Subject: Re: What debugger breakpoint features would you like?
Date: 04 Feb 2002 23:20:54 +0000
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/20.7

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

    Rob> Neil Jerram <address@hidden> writes:
    >> I don't think there's any problem per se in using traps.  The
    >> practical difficulty is that the raw evaluator-traps interface doesn't
    >> give any support for different applications (debugger, profiler, trace
    >> etc.) sharing those traps.  Part of what I'm trying to work out with
    >> the debugger work is what a sensible shareable traps interface might
    >> look like.  I'd definitely like/need to throw the needs of the
    >> profiler into that mix at some point.

    Rob> I think that's the problem I was remembering.  In general, for
    Rob> profiling, you'd like really quick responses, and you *need* to be
    Rob> able to minimize any skew the time spent in the infrastructure might
    Rob> introduce, though as long as you can successfully grab the time
    Rob> appropriately, you can subtract it out.

Thanks.  I'll bear this in mind if/when I get to looking at profiling.

    >> Not sure what you mean here; can you explain further?  Tail recursion
    >> certainly complicates the use of traps, but I think they give you just
    >> enough information to work everything out.

    Rob> I mean that if you want to write a profiler that uses function
    Rob> entry/exit traps to gather precise information (like gprof?), it seems
    Rob> like you might have a hard time doing that from the scheme level in
    Rob> guile, at least for tail recursive functions, without terribly skewing
    Rob> the results.  Though there may be a clever way around this, it seems
    Rob> like the naive way of grabbing the time when you enter the function,
    Rob> and then again when you exit it and subtracting, immediately turns a
    Rob> tail recursive function into one that grows the stack on every call.

    Rob> Though perhaps /me is missing something obvious :>

I still don't see the problem.  Suppose you're collecting entry count
and total time for functions in a program, putting the results into a
hash table that is keyed by a list of cars of expressions leading to
that function call.  [in reverse order]

E.g., if the program include

(define (facti n a)
  (if (= n 0)
      (facti (- n 1) (* a n))))

(define (fact2 n)
  (facti n 1))

then the key for <facti when called recursively by facti> would be
something like '(facti if facti fact2 ...), and the value for this key
would be 2-element vector containing hit count and accumulated time.
(Obviously you can collapse the set of keys in interesting ways in

You can do this using the enter-frame and exit-frame handlers:

(define *data* (make-hash-table ...))
(define *current-location* '())
(define *last-time* (now))

(define (assign-time-to-current-location)
  (let ((time (now))
        (locdata (hash-ref *data* *current-location*)))
    ;; Update hit count.
    (vector-set! locdata 0
                 (+ (vector-ref locdata 0) 1))
    ;; Update accumulated time.
    (vector-set! locdata 1
                 (+ (vector-ref locdata 1)
                    (- time *last-time*)))
    ;; Save current time.
    (set! *last-time* time)))

(define (enter-frame-handler ... tail? exp)
  (if tail?
      (set-car! *current-location* (car exp))
      (set! *current-location* (cons (car exp) *current-location*))))

(define (exit-frame-handler ...)
  (set! *current-location* (cdr *current-location*)))

This isn't quite right, because the keys for calls in tail position
are collapsed in the same way as tail call frames.  So let's say that,
whenever tail? is true, we also push the symbol 'tail? onto the key
stack.  Then the rule for the exit-frame handler is to keep popping
keys until it sees something other than 'tail?:

(define (enter-frame-handler ... tail? exp)
  (set! *current-location*
        (if tail?
            (cons 'tail? (cons (car exp) *current-location*))
            (cons (car exp) *current-location*))))

(define (exit-frame-handler ...)
  ;; Pop all the tail frames and then one non-tail frame.
  (set! *current-location*
        (let loop ((cl *current-location*))
          (cond ((null? cl) cl)
                ((eq? (car cl) 'tail?) (loop (cddr cl)))
                (else (cdr cl))))))

There's still the question of whether time spent in this code is
prejudicing the results, but (AFAIK, and I'm pretty sure) the
structure of this code has no effect on the evaluator handling tail
calls correctly.

Does this help at all?


reply via email to

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