#lang scheme/base
(require (file "util.scm"))
(provide profile
define-profile)
(define *PROFILE_NUM_CALLS* (make-hash))
(define *PROFILE_TOTAL_TIME* (make-hash))
(define-struct profile-frame (key entry-time subtract-time) #:mutable)
(define *PROFILE_CALL_STACK* '())
(define-syntax define-profile
(syntax-rules ()
((_ (fn arg ...) body ...)
(define (fn arg ...)
(profile :: fn begin body ...)))))
(define-syntax profile
(syntax-rules (::)
((_ :: profile-key-name f arg ...)
(let ((profile-key 'profile-key-name))
(when (empty? *PROFILE_CALL_STACK*)
(display (format "---Starting profile for '~A'---\n" profile-key)))
(inc-call-count-for! profile-key)
(start-timer-for! profile-key)
(let ((result (f arg ...)))
(stop-timer-for! profile-key)
(when (empty? *PROFILE_CALL_STACK*)
(display (format "~A\n---End profile for '~A'---\n\n"
(profile-data-str) profile-key))
(clear-profile-data!))
result)))
((_ f arg ...)
(profile :: f f arg ...))))
(define (clear-profile-data!)
(set! *PROFILE_NUM_CALLS* (make-hash))
(set! *PROFILE_TOTAL_TIME* (make-hash)))
(define (inc-call-count-for! profile-key)
(hash-set! *PROFILE_NUM_CALLS* profile-key
(+ 1 (hash-ref *PROFILE_NUM_CALLS* profile-key 0))))
(define (start-timer-for! profile-key)
(set! *PROFILE_CALL_STACK* (cons (make-profile-frame
profile-key (current-milliseconds) 0)
*PROFILE_CALL_STACK*)))
(define (stop-timer-for! profile-key)
(let* ((cur-frame (first *PROFILE_CALL_STACK*))
(cur-profile-key (profile-frame-key cur-frame))
(cur-entry-time (profile-frame-entry-time cur-frame))
(cur-sub-time (profile-frame-subtract-time cur-frame))
(cur-total-time (- (current-milliseconds) cur-entry-time)))
(set! *PROFILE_CALL_STACK* (rest *PROFILE_CALL_STACK*))
(unless (eq? cur-profile-key profile-key)
(e "Logic error with mis-matched profiling keys (~A and ~A)"
cur-profile-key profile-key))
(let ((adjusted-time (- cur-total-time cur-sub-time)))
(hash-set! *PROFILE_TOTAL_TIME* profile-key
(+ adjusted-time (hash-ref *PROFILE_TOTAL_TIME* profile-key 0)))
(unless (empty? *PROFILE_CALL_STACK*)
(let* ((parent-frame (first *PROFILE_CALL_STACK*))
(parent-sub-time (profile-frame-subtract-time parent-frame)))
(set-profile-frame-subtract-time! parent-frame
(+ parent-sub-time cur-total-time)))))))
(define (profile-data-str)
(string-join (hash-map *PROFILE_NUM_CALLS*
(lambda (k v)
(format "~A: ~A calls in ~A milliseconds"
k v (hash-ref *PROFILE_TOTAL_TIME* k))))
"\n"))