;;;; (statprof) -- a statistical profiler for Guile
;;;; -*-scheme-*-
;;;;
-;;;; Copyright (C) 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.
+;;;; Copyright (C) 2009, 2010, 2011, 2013-2015 Free Software Foundation, Inc.
;;;; Copyright (C) 2004, 2009 Andy Wingo <wingo at pobox dot com>
;;;; Copyright (C) 2001 Rob Browning <rlb at defaultvalue dot org>
;;;;
(let ((prev (setitimer ITIMER_PROF 0 0 0 usecs)))
(+ (* (caadr prev) #e1e6) (cdadr prev))))
-(define (profile-signal-handler sig)
- (define state (existing-profiler-state))
+(define profile-signal-handler
+ (let ()
+ (define (profile-signal-handler sig)
+ (define state (existing-profiler-state))
+
+ (set-inside-profiler?! state #t)
+
+ (when (positive? (profile-level state))
+ (let* ((stop-time (get-internal-run-time))
+ ;; Cut down to the signal handler. Note that this will
+ ;; only work if statprof.scm is compiled; otherwise we
+ ;; get `eval' on the stack instead, because if it's not
+ ;; compiled, profile-signal-handler is a thunk that
+ ;; tail-calls eval. For the same reason we define the
+ ;; handler in an inner letrec, so that the compiler sees
+ ;; the inner reference to profile-signal-handler as the
+ ;; same as the procedure, and therefore keeps slot 0
+ ;; alive. Nastiness, that.
+ (stack
+ (or (make-stack #t profile-signal-handler (outer-cut state))
+ (pk 'what! (make-stack #t)))))
+
+ (sample-stack-procs state stack)
+ (accumulate-time state stop-time)
+ (set-last-start-time! state (get-internal-run-time))
- (set-inside-profiler?! state #t)
-
- (when (positive? (profile-level state))
- (let* ((stop-time (get-internal-run-time))
- ;; Cut down to the signal handler. Note that this will only
- ;; work if statprof.scm is compiled; otherwise we get `eval'
- ;; on the stack instead, because if it's not compiled,
- ;; profile-signal-handler is a thunk that tail-calls eval.
- ;; Perhaps we should always compile the signal handler
- ;; instead.
- (stack (or (make-stack #t profile-signal-handler (outer-cut state))
- (pk 'what! (make-stack #t)))))
-
- (sample-stack-procs state stack)
- (accumulate-time state stop-time)
- (set-last-start-time! state (get-internal-run-time))
+ (reset-sigprof-timer (sampling-period state))))
- (reset-sigprof-timer (sampling-period state))))
-
- (set-inside-profiler?! state #f))
+ (set-inside-profiler?! state #f))
+ profile-signal-handler))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Count total calls.
(set-prev-sigprof-handler! state (car prev)))
(reset-sigprof-timer (if (zero? rpt) (sampling-period state) rpt))
(when (call-counts state)
- (add-hook! (vm-apply-hook) count-call))
- (set-vm-trace-level! (1+ (vm-trace-level)))
+ (add-hook! (vm-apply-hook) count-call)
+ (set-vm-trace-level! (1+ (vm-trace-level))))
#t)))
;; Do not call this from statprof internal functions -- user only.
;; signals here, but if I'm wrong, please let me know.
(set-profile-level! state (- (profile-level state) 1))
(when (zero? (profile-level state))
+ (when (call-counts state)
+ (set-vm-trace-level! (1- (vm-trace-level)))
+ (remove-hook! (vm-apply-hook) count-call))
(set-gc-time-taken! state
(- (assq-ref (gc-stats) 'gc-time-taken)
(gc-time-taken state)))
- (set-vm-trace-level! (1- (vm-trace-level)))
- (when (call-counts state)
- (remove-hook! (vm-apply-hook) count-call))
;; I believe that we need to do this before getting the time
;; (unless we want to make things even more complicated).
(set-remaining-prof-time! state (reset-sigprof-timer 0))
(visit-stacks (1+ pos) (cons (reverse stack) out))))))
(else (reverse out))))))
-(define (statprof-fold-call-data proc init)
+(define* (statprof-fold-call-data proc init #:optional
+ (state (existing-profiler-state)))
"Fold @var{proc} over the call-data accumulated by statprof. Cannot be
called while statprof is active. @var{proc} should take two arguments,
@code{(@var{call-data} @var{prior-result})}.
(lambda (key value prior-result)
(proc value prior-result))
init
- (stack-samples->procedure-data (existing-profiler-state))))
+ (stack-samples->procedure-data state)))
-(define (statprof-proc-call-data proc)
+(define* (statprof-proc-call-data proc #:optional
+ (state (existing-profiler-state)))
"Returns the call-data associated with @var{proc}, or @code{#f} if
none is available."
(when (statprof-active?)
(error "Can't call statprof-proc-call-data while profiler is running."))
- (hashv-ref (stack-samples->procedure-data (existing-profiler-state))
+ (hashv-ref (stack-samples->procedure-data state)
(cond
((primitive? proc) (procedure-name proc))
((program? proc) (program-code proc))
(proc-source (and=> (call-data-source call-data) source->string))
(self-samples (call-data-self-sample-count call-data))
(cum-samples (call-data-cum-sample-count call-data))
- (all-samples (statprof-sample-count))
- (secs-per-sample (/ (statprof-accumulated-time)
- (statprof-sample-count)))
+ (all-samples (statprof-sample-count state))
+ (secs-per-sample (/ (statprof-accumulated-time state)
+ (statprof-sample-count state)))
(num-calls (and (call-counts state)
(statprof-call-data-calls call-data))))
"Displays a gprof-like summary of the statistics collected. Unless an
optional @var{port} argument is passed, uses the current output port."
(cond
- ((zero? (statprof-sample-count))
+ ((zero? (statprof-sample-count state))
(format port "No samples recorded.\n"))
(else
(let* ((stats-list (statprof-fold-call-data
(lambda (data prior-value)
(cons (statprof-call-data->stats data)
prior-value))
- '()))
+ '()
+ state))
(sorted-stats (sort stats-list stats-sorter)))
(define (display-stats-line stats)
(for-each display-stats-line sorted-stats)
(display "---\n" port)
- (simple-format #t "Sample count: ~A\n" (statprof-sample-count))
+ (simple-format #t "Sample count: ~A\n" (statprof-sample-count state))
(simple-format #t "Total time: ~A seconds (~A seconds in GC)\n"
- (statprof-accumulated-time)
+ (statprof-accumulated-time state)
(/ (gc-time-taken state)
1.0 internal-time-units-per-second))))))
(call-data-name data)
(call-data-call-count data)
(call-data-cum-sample-count data))))
- #f)
- (simple-format #t "Total time: ~A\n" (statprof-accumulated-time))
- (simple-format #t "Sample count: ~A\n" (statprof-sample-count)))
+ #f
+ state)
+ (simple-format #t "Total time: ~A\n" (statprof-accumulated-time state))
+ (simple-format #t "Sample count: ~A\n" (statprof-sample-count state)))
(define (statprof-display-anomolies)
(issue-deprecation-warning "statprof-display-anomolies is a misspelling. "
equal?))))
(define (call-thunk thunk)
- (thunk)
- (values))
+ (call-with-values (lambda () (thunk))
+ (lambda results
+ (apply values results))))
(define* (statprof thunk #:key (loop 1) (hz 100) (count-calls? #f)
(port (current-output-port)) full-stacks?)
- "Profiles the execution of @var{thunk}.
+ "Profile the execution of @var{thunk}, and return its return values.
-The stack will be sampled @var{hz} times per second, and the thunk itself will
-be called @var{loop} times.
+The stack will be sampled @var{hz} times per second, and the thunk
+itself will be called @var{loop} times.
If @var{count-calls?} is true, all procedure calls will be recorded. This
operation is somewhat expensive."
(let ((state (fresh-profiler-state #:count-calls? count-calls?
#:sampling-period
(inexact->exact (round (/ 1e6 hz)))
- #:outer-cut call-thunk)))
+ #:outer-cut
+ (program-address-range call-thunk))))
(parameterize ((profiler-state state))
(dynamic-wind
(lambda ()
(statprof-start state))
(lambda ()
(let lp ((i loop))
- (unless (zero? i)
+ (unless (= i 1)
(call-thunk thunk)
- (lp (1- i)))))
+ (lp (1- i))))
+ (call-thunk thunk))
(lambda ()
(statprof-stop state)
(statprof-display port state))))))
(define-macro (with-statprof . args)
- "Profiles the expressions in its body.
+ "Profile the expressions in the body, and return the body's return values.
Keyword arguments:
#:count-calls? ,(kw-arg-ref #:count-calls? args #f)
#:full-stacks? ,(kw-arg-ref #:full-stacks? args #f)))
-(define* (gcprof thunk #:key (loop 1) full-stacks?)
+(define* (gcprof thunk #:key (loop 1) full-stacks? (port (current-output-port)))
"Do an allocation profile of the execution of @var{thunk}.
The stack will be sampled soon after every garbage collection, yielding
@var{loop} parameter, to cause @var{thunk} to be called @var{loop}
times."
- (let ((state (fresh-profiler-state #:outer-cut call-thunk)))
+ (let ((state (fresh-profiler-state #:outer-cut
+ (program-address-range call-thunk))))
(parameterize ((profiler-state state))
(define (gc-callback)
(unless (inside-profiler? state)
(gc-time-taken state)))
(accumulate-time state (get-internal-run-time))
(set-profile-level! state 0)
- (statprof-display))))))
+ (statprof-display port state))))))