
(define ($just-file fn l)
  (filter (lambda (x) x)
          (map (lambda (x)
	         (define p (list->string (subst #\\ #\/ (string->list (cadr x)))))
                 (and (or (equal? p fn)
                          (equal? p (find-source fn)))
                      (list* (car x) fn (cddr x))))
               l)))

(mat compile-profile
  (error? ; invalid argument
    (compile-profile 'src))
  (eqv?
    (parameterize ([compile-profile #t])
      (compile-profile))
    'source)
  (eqv?
    (parameterize ([compile-profile 'source])
      (compile-profile))
    'source)
  (eqv?
    (parameterize ([compile-profile 'block])
      (compile-profile))
    'block)
  (error? ; incorrect argument count
    (profile-dump '()))
  (error? ; incorrect argument count
    (profile-clear '()))
  (error? ; incorrect argument count
    (profile-dump-list #t '() 3))
  (error? ; invalid dump
    (profile-dump-list #f 17))
  (error? ; invalid dump
    (profile-dump-list #f '(17)))
  (error? ; invalid dump
    (profile-dump-list #f '((a . 17))))
  (error? ; invalid dump
    (profile-dump-list #f `((,(make-source-object (source-file-descriptor "abc" 34) 0 3) . q))))
  (error? ; incorrect argument count
    (profile-dump-html "" '() 3))
  (error? ; not a string
    (profile-dump-html '(prefix)))
  (error? ; invalid dump
    (profile-dump-html "profile" 17))
  (error? ; invalid dump
    (profile-dump-html "profile" '(17)))
  (error? ; invalid dump
    (profile-dump-html "profile" '((a . 17))))
  (error? ; invalid dump
    (profile-dump-html "profile" `((,(make-source-object (source-file-descriptor "abc" 34) 0 3) . q))))
  (error? ; incorrect argument count
    (profile-dump-data))
  (error? ; incorrect argument count
    (profile-dump-data "profile.data" '() 'q))
  (error? ; not a string
    (profile-dump-data #t))
  (error? ; invalid dump
    (profile-dump-data "profile.data" 17))
  (error? ; invalid dump
    (profile-dump-data "profile.data" '(17)))
  (error? ; invalid dump
    (profile-dump-data "profile.data" '((a . 17))))
  (error? ; invalid dump
    (profile-dump-data "profile.data" `((,(make-source-object (source-file-descriptor "abc" 34) 0 3) . q))))
  (error? ; not a string
    (profile-load-data 'what?))
  (eqv? (parameterize ([compile-profile #t])
          (compile
            '(let ()
               (define (f x) (if (= x 0) 1 (* x (f (- x 1)))))
               (f 3))))
        6)
  (eqv? (parameterize ([compile-profile #t])
          (compile
            '(let ()
               (define fat+
                 (lambda (x y)
                   (if (zero? y)
                       x
                       (fat+ (1+ x) (1- y)))))
               (define fatfib
                 (lambda (x)
                   (if (< x 2)
                       1
                       (fat+ (fatfib (1- x)) (fatfib (1- (1- x)))))))
               (fatfib 20))))
         10946)
  (equal?
    (parameterize ([compile-profile #t])
      (compile
        '(let ()
           (define $values (lambda (n) (lambda () (apply values (make-list n)))))
           (define foo
             (lambda (n)
               (call/cc
                 (lambda (k)
                   (with-exception-handler
                     (lambda (c) (collect) (k 'okay))
                     (lambda ()
                       (define f (case-lambda))
                       (let ([x (random 10)])
                         (call-with-values ($values n) f))))))))
           (list (foo 0) (foo 1) (foo 3) (foo 10) (foo 100) (foo 1000)))))
    '(okay okay okay okay okay okay))
  ; no longer recording (useless) profiling information when source file & position aren't available
  #;(let ([ls (profile-dump)])
    (and (list? ls)
      (not (null? ls))))
  (eqv? (profile-clear) (void))
  (or (eq? (compile-profile) 'source) (andmap zero? (map cdr (remp preexisting-profile-dump-entry? (profile-dump)))))
  (begin (set! cp-fatfib (void)) #t) ; release fatfib

  (begin (define $old-cp (compile-profile)) #t)
  ; this collect is here to make it more likely that we won't get a generation 1
  ; collection cementing in place the code that defines cp-fact
  (begin (collect 1) #t)
  (mat/cf (testfile "testfile")
    (eval-when (compile) (compile-profile 'source))
    (define (cp-fact x) (if (= x 0) 1 (* x (cp-fact (- x 1))))))
  (eq? (compile-profile) $old-cp)
  ; drop code that defines cp-fact so it won't show up in profile-dump-list in
  ; hopes of resolving potential issue with comparison to pdl further down
  (begin (collect (collect-maximum-generation)) #t)
  (= (cp-fact 10) 3628800)
  (begin
    (define (suffix=? suffix s)
      (let ([n (string-length suffix)]
            [len (string-length s)])
        (and (>= len n) (string=? suffix (substring s (- len n) len)))))
    (define (sdir? x) (not (suffix=? "testfile.ss" (cadr x))))
    (define-values (pdl pdl2)
      (with-interrupts-disabled
        (values
          (remp sdir? (profile-dump-list #t (profile-dump)))
          (remp sdir? (profile-dump-list)))))
    #t)
  (equal? pdl pdl2)
  (not (null? pdl))
  (begin
    (rm-rf "testdir")
    (mkdir "testdir")
    (parameterize ([gensym-prefix 0]) (profile-dump-html "testdir/" (profile-dump)))
    #t)
  (file-exists? "testdir/profile.html")
  (file-exists? "testdir/testfile.ss.html")

  (begin (define $old-cp (compile-profile)) #t)
  (mat/cf (testfile "testfile-block")
    (eval-when (compile) (compile-profile 'block))
    (define (cp-fact-block x) (if (= x 0) 1 (* x (cp-fact-block (- x 1))))))
  (eq? (compile-profile) $old-cp)
  (= (cp-fact-block 10) 3628800)
  (or (equal? (compile-profile) 'source)
      (equal?
        (remp sdir? (profile-dump-list))
        pdl))
  (begin
    (profile-dump-html)
    #t)
  (file-exists? "profile.html")
  (file-exists? "testfile.ss.html")
  (not (file-exists? "testfile2.ss.html"))

  (eqv? (profile-clear) (void))

  (mat/cf (testfile "testfile")
    (eval-when (compile) (compile-profile #t))
    (define (cp-fact x) (if (= x 0) 1 (* x (cp-fact (- x 1))))))
  (= (cp-fact 10) 3628800)
  (eqv? (profile-dump-data "testfile1.pd" (remp preexisting-profile-dump-entry? (profile-dump))) (void))
  (file-exists? "testfile1.pd")
  (eqv? (profile-load-data) (void))
  (eqv? (profile-load-data "testfile1.pd") (void))
  (begin
    (define $cp-ip (open-file-input-port "testfile.ss"))
    (define $cp-sfd (make-source-file-descriptor "testfile.ss" $cp-ip))
    (define $qw (lambda (bfp efp) (profile-query-weight (make-source-object $cp-sfd bfp efp))))
    #t)

  (eqv? (close-port $cp-ip) (void))

  (eqv? ($qw 0 0) 0.0) ; bfp, efp combination not in database
  (eqv? ; file not in database
    (let* ([fn (find-source "main.zuo")]
           [ip (open-file-input-port fn)]
           [sfd (make-source-file-descriptor fn ip)])
      (close-port ip)
      (profile-query-weight (make-source-object sfd 0 0)))
    #f)
  ((lambda (x) (and (memv x (list 0.0 (/ 1.0 11.0))) #t)) ($qw 0 42))
  ((lambda (x) (and (memv x (list 0.0 (/ 1.0 11.0))) #t)) ($qw 43 102))
  (eqv? ($qw 63 101) 1.0)
  (eqv? ($qw 75 76) (fl/ 1.0 11.0))
  (eqv? ($qw 77 100) (fl/ 10.0 11.0))
  ((lambda (x) (and (memv x (list 0.0 (/ 1.0 11.0))) #t)) ($qw 103 127))
  (eqv? ($qw 119 126) 0.0)
  (eqv? ($qw 120 125) 0.0)
  (eqv? (profile-clear) (void))
  (= (cp-fact 5) 120)
  (eqv? (profile-dump-data "testfile2.pd" (remp preexisting-profile-dump-entry? (profile-dump))) (void))
  (eqv? (profile-load-data "testfile2.pd") (void))
  ((lambda (x) (and (memv x (list 0.0 (/ 1.0 11.0) (fl/ (/ 1.0 11.0) 2.0))) #t)) ($qw 0 42))
  (eqv? ($qw 21 40) 0.0)
  ((lambda (x) (and (memv x (list 0.0 (/ 1.0 11.0) (fl/ (/ 1.0 11.0) 2.0))) #t)) ($qw 43 102))
  (eqv? ($qw 63 101) 1.0)
  (eqv? ($qw 75 76) (fl/ (fl+ (/ 1.0 11.0) (fl/ 1.0 6.0)) 2.0))
  (eqv? ($qw 77 100) (fl/ (fl+ (fl/ 10.0 11.0) (fl/ 5.0 6.0)) 2.0))
  ((lambda (x) (and (memv x (list 0.0 (/ 1.0 11.0) (fl/ (/ 1.0 11.0) 2.0))) #t)) ($qw 103 127))
  (eqv? ($qw 119 126) 0.0)
  (eqv? ($qw 120 125) 0.0)
  (eqv? (profile-clear) (void))

  ; make sure all is well when compiled with source profile info
  (mat/cf (testfile "testfile")
    (eval-when (compile) (compile-profile 'block))
    (define (cp-fact x) (if (= x 0) 1 (* x (cp-fact (- x 1))))))
  (eqv? (profile-dump-data "testfile3.pd" (remp preexisting-profile-dump-entry? (profile-dump))) (void))
  (file-exists? "testfile3.pd")
  (eqv? (profile-load-data "testfile3.pd") (void))
  ; and again with block profile info
  (mat/cf (testfile "testfile")
    (eval-when (compile) (compile-profile #f))
    (define (cp-fact x) (if (= x 0) 1 (* x (cp-fact (- x 1))))))
  (= (cp-fact 5) 120)

  (eqv? (profile-clear-database) (void))
  (eqv? ($qw 0 42) #f)
  (eqv? ($qw 77 100) #f)

  ; make sure record-ref, record-type, and record-cd are properly handled by
  ; find-source in pdhtml
  (mat/cf
    (eval-when (compile) (compile-profile #t))
    (library (A) (export make-foo foo? foo-x) (import (chezscheme)) (define-record-type foo (fields x)))
    (let ()
      (import (A))
      (define add-foo-xs
        (lambda ls
          (let f ([ls ls] [sum 0])
            (if (null? ls) sum (f (cdr ls) (+ (foo-x (car ls)) sum))))))
      ; make sure this is still around when we call profile-dump-list
      (set! $add-foo-xs add-foo-xs)
      (pretty-print (add-foo-xs (make-foo 1) (make-foo 2) (make-foo 3)))))
  (not (null? (profile-dump-list)))
  (eqv? (profile-clear) (void))
  (begin (set! $add-foo-xs #f) #t)

  (vector? (profile-palette))
  (vector?
    (parameterize ([profile-palette (vector-map
                                      (lambda (p) (cons "white" (car p)))
                                      (profile-palette))])
      (profile-palette)))
  (parameterize ([profile-palette
                  '#(("black" . "white")
                     ("red" . "white")
                     ("blue" . "black"))])
    (= (vector-length (profile-palette)) 3))
  (error? (profile-palette '#()))
  (error? (profile-palette '#(("black" . "white"))))
  (error? (profile-palette '#(("black" . "white") ("red" . "white"))))
  (error?
    (profile-palette
      '#(("black" . "white")
         #("red" "white")
         ("blue" . "black"))))
  (error?
    (profile-palette
      '#(("black" . "white")
         ("red" . "white")
         ("blue" . black))))
  (error?
    (profile-palette
      '#(("black" . "white")
         ("red" . "white")
         (#x0000ff . "black"))))
  ; test for proper counts in the presence of control operators
  (begin
    (define $return)
    (define $retry)
    (with-output-to-file "testfile-cp1.ss"
      (lambda ()
        (display-string "\
(define $frumble
  (lambda (ls)
    (if (null? ls)
        1
        (let ([n (car ls)])
          (if (eqv? n 0)
              (call/cc (lambda (k) (set! $retry k) ($return 0)))
              (let ([q ($frumble (cdr ls))])
                (add1 (* q n))))))))
"))
      'replace)
    (profile-clear)
    (parameterize ([enable-cp0 #t] [#%$suppress-primitive-inlining #f] [optimize-level 2] [compile-profile #t])
      (load "testfile-cp1.ss" compile))
    #t)
  (eqv?
    ($frumble (make-list 100 5))
    9860761315262647567646607066034827870915080438862787559628486633300781)
  (equal?
    ($just-file "testfile-cp1.ss" (profile-dump-list))
    '((101 "testfile-cp1.ss" 36 258 3 5)
      (101 "testfile-cp1.ss" 40 50 3 9)
      (101 "testfile-cp1.ss" 41 46 3 10)
      (101 "testfile-cp1.ss" 47 49 3 16)
      (100 "testfile-cp1.ss" 69 257 5 9)
      (100 "testfile-cp1.ss" 78 86 5 18)
      (100 "testfile-cp1.ss" 79 82 5 19)
      (100 "testfile-cp1.ss" 83 85 5 23)
      (100 "testfile-cp1.ss" 99 256 6 11)
      (100 "testfile-cp1.ss" 103 113 6 15)
      (100 "testfile-cp1.ss" 104 108 6 16)
      (100 "testfile-cp1.ss" 109 110 6 21)
      (100 "testfile-cp1.ss" 111 112 6 23)
      (100 "testfile-cp1.ss" 193 255 8 15)
      (100 "testfile-cp1.ss" 202 221 8 24)
      (100 "testfile-cp1.ss" 203 211 8 25)
      (100 "testfile-cp1.ss" 212 220 8 34)
      (100 "testfile-cp1.ss" 213 216 8 35)
      (100 "testfile-cp1.ss" 217 219 8 39)
      (100 "testfile-cp1.ss" 240 254 9 17)
      (100 "testfile-cp1.ss" 241 245 9 18)
      (100 "testfile-cp1.ss" 246 253 9 23)
      (100 "testfile-cp1.ss" 247 248 9 24)
      (100 "testfile-cp1.ss" 249 250 9 26)
      (100 "testfile-cp1.ss" 251 252 9 28)
      (1 "testfile-cp1.ss" 0 260 1 1)
      (1 "testfile-cp1.ss" 19 259 2 3)
      (1 "testfile-cp1.ss" 59 60 4 9)
      (0 "testfile-cp1.ss" 128 178 7 15)
      (0 "testfile-cp1.ss" 129 136 7 16)
      (0 "testfile-cp1.ss" 137 177 7 24)
      (0 "testfile-cp1.ss" 149 164 7 36)
      (0 "testfile-cp1.ss" 162 163 7 49)
      (0 "testfile-cp1.ss" 165 176 7 52)
      (0 "testfile-cp1.ss" 166 173 7 53)
      (0 "testfile-cp1.ss" 174 175 7 61)))
  (eqv? 
    (call/cc
      (lambda (k)
        (set! $return k)
        (let ([ans ($frumble (append (make-list 50 5) (list 0) (make-list 50 7)))])
          ($return ans))))
    0)
  (equal?
    ($just-file "testfile-cp1.ss" (profile-dump-list))
    '((152 "testfile-cp1.ss" 36 258 3 5)
      (152 "testfile-cp1.ss" 40 50 3 9)
      (152 "testfile-cp1.ss" 41 46 3 10)
      (152 "testfile-cp1.ss" 47 49 3 16)
      (151 "testfile-cp1.ss" 69 257 5 9)
      (151 "testfile-cp1.ss" 78 86 5 18)
      (151 "testfile-cp1.ss" 79 82 5 19)
      (151 "testfile-cp1.ss" 83 85 5 23)
      (151 "testfile-cp1.ss" 99 256 6 11)
      (151 "testfile-cp1.ss" 103 113 6 15)
      (151 "testfile-cp1.ss" 104 108 6 16)
      (151 "testfile-cp1.ss" 109 110 6 21)
      (151 "testfile-cp1.ss" 111 112 6 23)
      (150 "testfile-cp1.ss" 193 255 8 15)
      (150 "testfile-cp1.ss" 202 221 8 24)
      (150 "testfile-cp1.ss" 203 211 8 25)
      (150 "testfile-cp1.ss" 212 220 8 34)
      (150 "testfile-cp1.ss" 213 216 8 35)
      (150 "testfile-cp1.ss" 217 219 8 39)
      (100 "testfile-cp1.ss" 240 254 9 17)
      (100 "testfile-cp1.ss" 241 245 9 18)
      (100 "testfile-cp1.ss" 246 253 9 23)
      (100 "testfile-cp1.ss" 247 248 9 24)
      (100 "testfile-cp1.ss" 249 250 9 26)
      (100 "testfile-cp1.ss" 251 252 9 28)
      (1 "testfile-cp1.ss" 0 260 1 1)
      (1 "testfile-cp1.ss" 19 259 2 3)
      (1 "testfile-cp1.ss" 59 60 4 9)
      (1 "testfile-cp1.ss" 128 178 7 15)
      (1 "testfile-cp1.ss" 129 136 7 16)
      (1 "testfile-cp1.ss" 137 177 7 24)
      (1 "testfile-cp1.ss" 149 164 7 36)
      (1 "testfile-cp1.ss" 162 163 7 49)
      (1 "testfile-cp1.ss" 165 176 7 52)
      (1 "testfile-cp1.ss" 166 173 7 53)
      (1 "testfile-cp1.ss" 174 175 7 61)))
  (eqv?
    (call/cc
      (lambda (k)
        (set! $return k)
        ($retry 1)))
    111022302462515654042363166809082031)
  (equal?
    ($just-file "testfile-cp1.ss" (profile-dump-list))
    '((152 "testfile-cp1.ss" 36 258 3 5)
      (152 "testfile-cp1.ss" 40 50 3 9)
      (152 "testfile-cp1.ss" 41 46 3 10)
      (152 "testfile-cp1.ss" 47 49 3 16)
      (151 "testfile-cp1.ss" 69 257 5 9)
      (151 "testfile-cp1.ss" 78 86 5 18)
      (151 "testfile-cp1.ss" 79 82 5 19)
      (151 "testfile-cp1.ss" 83 85 5 23)
      (151 "testfile-cp1.ss" 99 256 6 11)
      (151 "testfile-cp1.ss" 103 113 6 15)
      (151 "testfile-cp1.ss" 104 108 6 16)
      (151 "testfile-cp1.ss" 109 110 6 21)
      (151 "testfile-cp1.ss" 111 112 6 23)
      (150 "testfile-cp1.ss" 193 255 8 15)
      (150 "testfile-cp1.ss" 202 221 8 24)
      (150 "testfile-cp1.ss" 203 211 8 25)
      (150 "testfile-cp1.ss" 212 220 8 34)
      (150 "testfile-cp1.ss" 213 216 8 35)
      (150 "testfile-cp1.ss" 217 219 8 39)
      (150 "testfile-cp1.ss" 240 254 9 17)
      (150 "testfile-cp1.ss" 241 245 9 18)
      (150 "testfile-cp1.ss" 246 253 9 23)
      (150 "testfile-cp1.ss" 247 248 9 24)
      (150 "testfile-cp1.ss" 249 250 9 26)
      (150 "testfile-cp1.ss" 251 252 9 28)
      (1 "testfile-cp1.ss" 0 260 1 1)
      (1 "testfile-cp1.ss" 19 259 2 3)
      (1 "testfile-cp1.ss" 59 60 4 9)
      (1 "testfile-cp1.ss" 128 178 7 15)
      (1 "testfile-cp1.ss" 129 136 7 16)
      (1 "testfile-cp1.ss" 137 177 7 24)
      (1 "testfile-cp1.ss" 149 164 7 36)
      (1 "testfile-cp1.ss" 162 163 7 49)
      (1 "testfile-cp1.ss" 165 176 7 52)
      (1 "testfile-cp1.ss" 166 173 7 53)
      (1 "testfile-cp1.ss" 174 175 7 61)))
  (begin
    (collect (collect-maximum-generation)) ; drop code object for the define and lambda
    (profile-release-counters) ; drop proile information for the dropped code object
    #t)
  (equal?
    ($just-file "testfile-cp1.ss" (profile-dump-list))
    '((152 "testfile-cp1.ss" 36 258 3 5)
      (152 "testfile-cp1.ss" 40 50 3 9)
      (152 "testfile-cp1.ss" 41 46 3 10)
      (152 "testfile-cp1.ss" 47 49 3 16)
      (151 "testfile-cp1.ss" 69 257 5 9)
      (151 "testfile-cp1.ss" 78 86 5 18)
      (151 "testfile-cp1.ss" 79 82 5 19)
      (151 "testfile-cp1.ss" 83 85 5 23)
      (151 "testfile-cp1.ss" 99 256 6 11)
      (151 "testfile-cp1.ss" 103 113 6 15)
      (151 "testfile-cp1.ss" 104 108 6 16)
      (151 "testfile-cp1.ss" 109 110 6 21)
      (151 "testfile-cp1.ss" 111 112 6 23)
      (150 "testfile-cp1.ss" 193 255 8 15)
      (150 "testfile-cp1.ss" 202 221 8 24)
      (150 "testfile-cp1.ss" 203 211 8 25)
      (150 "testfile-cp1.ss" 212 220 8 34)
      (150 "testfile-cp1.ss" 213 216 8 35)
      (150 "testfile-cp1.ss" 217 219 8 39)
      (150 "testfile-cp1.ss" 240 254 9 17)
      (150 "testfile-cp1.ss" 241 245 9 18)
      (150 "testfile-cp1.ss" 246 253 9 23)
      (150 "testfile-cp1.ss" 247 248 9 24)
      (150 "testfile-cp1.ss" 249 250 9 26)
      (150 "testfile-cp1.ss" 251 252 9 28)
      (1 "testfile-cp1.ss" 59 60 4 9)
      (1 "testfile-cp1.ss" 128 178 7 15)
      (1 "testfile-cp1.ss" 129 136 7 16)
      (1 "testfile-cp1.ss" 137 177 7 24)
      (1 "testfile-cp1.ss" 149 164 7 36)
      (1 "testfile-cp1.ss" 162 163 7 49)
      (1 "testfile-cp1.ss" 165 176 7 52)
      (1 "testfile-cp1.ss" 166 173 7 53)
      (1 "testfile-cp1.ss" 174 175 7 61)))
  ; test profiling with compiled files
  (begin
    (with-output-to-file "testfile-cp2.ss"
      (lambda ()
        (display-string "\
(define cp2-fib
  (rec fib
    (lambda (n)
      (cond
        [(fx= n 0) 1]
        [(fx= n 1) 1]
        [else (+ (fib (- n 1)) (fib (- n 2)))]))))
"))
      'replace)
    (parameterize ([enable-cp0 #t] [#%$suppress-primitive-inlining #f] [optimize-level 2] [compile-profile #t])
      (compile-file "testfile-cp2"))
    (profile-clear)
    (load "testfile-cp2.so")
    #t)
  (eqv? (cp2-fib 10) 89)
  (equal?
    ($just-file "testfile-cp2.ss" (profile-dump-list))
    '((177 "testfile-cp2.ss" 49 146 4 7)
      (177 "testfile-cp2.ss" 64 73 5 10)
      (177 "testfile-cp2.ss" 65 68 5 11)
      (177 "testfile-cp2.ss" 69 70 5 15)
      (177 "testfile-cp2.ss" 71 72 5 17)
      (143 "testfile-cp2.ss" 86 95 6 10)
      (143 "testfile-cp2.ss" 87 90 6 11)
      (143 "testfile-cp2.ss" 91 92 6 15)
      (143 "testfile-cp2.ss" 93 94 6 17)
      (88 "testfile-cp2.ss" 113 144 7 15)
      (88 "testfile-cp2.ss" 114 115 7 16)
      (88 "testfile-cp2.ss" 116 129 7 18)
      (88 "testfile-cp2.ss" 117 120 7 19)
      (88 "testfile-cp2.ss" 121 128 7 23)
      (88 "testfile-cp2.ss" 122 123 7 24)
      (88 "testfile-cp2.ss" 124 125 7 26)
      (88 "testfile-cp2.ss" 126 127 7 28)
      (88 "testfile-cp2.ss" 130 143 7 32)
      (88 "testfile-cp2.ss" 131 134 7 33)
      (88 "testfile-cp2.ss" 135 142 7 37)
      (88 "testfile-cp2.ss" 136 137 7 38)
      (88 "testfile-cp2.ss" 138 139 7 40)
      (88 "testfile-cp2.ss" 140 141 7 42)
      (55 "testfile-cp2.ss" 96 97 6 20)
      (34 "testfile-cp2.ss" 74 75 5 20)
      (1 "testfile-cp2.ss" 0 149 1 1)
      (1 "testfile-cp2.ss" 18 148 2 3)
      (1 "testfile-cp2.ss" 23 26 2 8)
      (1 "testfile-cp2.ss" 31 147 3 5)))
  (begin
    (collect (collect-maximum-generation)) ; drop code object for the define and lambda
    (profile-release-counters) ; drop proile information for the dropped code object
    #t)
  (equal?
    ($just-file "testfile-cp2.ss" (profile-dump-list))
    '((177 "testfile-cp2.ss" 49 146 4 7)
      (177 "testfile-cp2.ss" 64 73 5 10)
      (177 "testfile-cp2.ss" 65 68 5 11)
      (177 "testfile-cp2.ss" 69 70 5 15)
      (177 "testfile-cp2.ss" 71 72 5 17)
      (143 "testfile-cp2.ss" 86 95 6 10)
      (143 "testfile-cp2.ss" 87 90 6 11)
      (143 "testfile-cp2.ss" 91 92 6 15)
      (143 "testfile-cp2.ss" 93 94 6 17)
      (88 "testfile-cp2.ss" 113 144 7 15)
      (88 "testfile-cp2.ss" 114 115 7 16)
      (88 "testfile-cp2.ss" 116 129 7 18)
      (88 "testfile-cp2.ss" 117 120 7 19)
      (88 "testfile-cp2.ss" 121 128 7 23)
      (88 "testfile-cp2.ss" 122 123 7 24)
      (88 "testfile-cp2.ss" 124 125 7 26)
      (88 "testfile-cp2.ss" 126 127 7 28)
      (88 "testfile-cp2.ss" 130 143 7 32)
      (88 "testfile-cp2.ss" 131 134 7 33)
      (88 "testfile-cp2.ss" 135 142 7 37)
      (88 "testfile-cp2.ss" 136 137 7 38)
      (88 "testfile-cp2.ss" 138 139 7 40)
      (88 "testfile-cp2.ss" 140 141 7 42)
      (55 "testfile-cp2.ss" 96 97 6 20)
      (34 "testfile-cp2.ss" 74 75 5 20)))
  (eqv? (profile-clear) (void))
  (begin
    (with-output-to-file "testfile.ss"
      (lambda ()
        (pretty-print
          '(define f (lambda () 0))))
      'replace)
    (parameterize ([compile-profile #t]) (load "testfile.ss" compile))
    #t)
  (begin
    (with-output-to-file "testfile.ss"
      (lambda ()
        (pretty-print
          '(define f (lambda () 1))))
      'replace)
    #t)
  (eqv? (f) 0)
  (warning? ; unmodified source file not found
    (parameterize ([source-directories (cons* "../s" "../unicode" (source-directories))])
      (profile-dump-list)))
  (warning? ; unmodified source file not found
    (parameterize ([source-directories (cons* "../s" "../unicode" (source-directories))])
      (profile-dump-list #t)))
  (warning? ; unmodified source file not found
    (parameterize ([source-directories (cons* "../s" "../unicode" (source-directories))])
      (profile-dump-list #t (profile-dump))))
  (warning? ; unmodified source file not found
    (parameterize ([source-directories (cons* "../s" "../unicode" (source-directories))])
      (profile-dump-list #t (profile-dump))))
  (guard (c [else #f])
    (profile-dump-list #f)
    #t)
  (guard (c [else #f])
    (profile-dump-list #f (profile-dump))
    #t)
  (eqv? (profile-clear) (void))

  ; verify that annotations are preserved within syntax objects when
  ; profiling is enabled even when generation of inspector information
  ; is disabled.
  (begin
    (mkfile "testfile-ca3.ss"
      '(library (testfile-ca3) (export a) (import (chezscheme))
         (define-syntax a (lambda (x) #'(cons 0 1)))))
    (mkfile "testfile-cp3.ss"
      '(import (chezscheme) (testfile-ca3))
      '(do ([i 123 (fx- i 1)] [q #f a]) ((fx= i 0) (pretty-print q)))
      '(profile-dump-html))
    (separate-compile
      '(lambda (x)
         (parameterize ([generate-inspector-information #f]
                        [compile-profile #t])
           (compile-library x)))
      'ca3)
    (separate-compile
      '(lambda (x)
         (parameterize ([compile-profile #t])
           (compile-program x)))
      'cp3)
    #t)
  (equal?
    (separate-eval
      '(load-program "testfile-cp3.so")
      '(cdr (find (lambda (x) (equal? (source-file-descriptor-path (source-object-sfd (car x))) "testfile-ca3.ss")) (profile-dump))))
    "(0 . 1)\n123\n")

  ; verify that we get profiling information for local macro transformers
  (begin
    (call-with-port (open-output-file "testfile-cp4.ss" 'replace)
      (lambda (op)
        (put-string op "\
(let ()
  (define-syntax a
    (lambda (q)
      (define square
        (lambda (n)
          (* n n)))
      (syntax-case q ()
        [(_ x (d ...) e)
         #`(let ([x (quote #,(map square (datum (d ...))))])
             e)])))
  (pretty-print (list (a b (8 6 7) b) (a b (5 3 0 9) (list b)))))")))
    (delete-file "testfile-cp4.so")
    (parameterize ([print-gensym #f] [current-eval compile] [compile-profile #t])
      (compile-file "testfile-cp4"))
    #t)
  (equal?
    (sort (lambda (x y) (< (list-ref x 2) (list-ref y 2)))
          ($just-file "testfile-cp4.ss" (profile-dump-list)))
    '((1 "testfile-cp4.ss" 31 232 3 5)     ; first transformer count ...
      (2 "testfile-cp4.ss" 72 102 5 9)
      (7 "testfile-cp4.ss" 94 101 6 11)
      (7 "testfile-cp4.ss" 95 96 6 12)
      (7 "testfile-cp4.ss" 97 98 6 14)
      (7 "testfile-cp4.ss" 99 100 6 16)
      (2 "testfile-cp4.ss" 110 231 7 7)
      (2 "testfile-cp4.ss" 123 124 7 20)
      (2 "testfile-cp4.ss" 162 229 9 10)
      (2 "testfile-cp4.ss" 182 210 9 30)
      (2 "testfile-cp4.ss" 183 186 9 31)
      (2 "testfile-cp4.ss" 187 193 9 35)
      (2 "testfile-cp4.ss" 194 209 9 42)   ; ... last transformer count
      ))
  (begin
    (collect (collect-maximum-generation))
    (profile-release-counters)
    #t)
  (equal?
    (with-output-to-string
      (lambda ()
        (revisit "testfile-cp4.so")))
    "((64 36 49) ((25 9 0 81)))\n")
  (equal?
    (sort (lambda (x y) (< (list-ref x 2) (list-ref y 2)))
          ($just-file "testfile-cp4.ss" (profile-dump-list)))
    '((1 "testfile-cp4.ss" 0 299 1 1)      ; top-level let
      (1 "testfile-cp4.ss" 236 298 11 3)   ; pretty-print call ...
      (1 "testfile-cp4.ss" 237 249 11 4)   ; ... and subforms
      (1 "testfile-cp4.ss" 250 297 11 17)
      (1 "testfile-cp4.ss" 251 255 11 18)
      (1 "testfile-cp4.ss" 256 271 11 23)
      (1 "testfile-cp4.ss" 269 270 11 36)
      (1 "testfile-cp4.ss" 272 296 11 39)
      (1 "testfile-cp4.ss" 287 295 11 54)
      (1 "testfile-cp4.ss" 288 292 11 55)
      (1 "testfile-cp4.ss" 293 294 11 60)
      ))
)

(mat profile-form
  (error? ; invalid syntax
    (profile))
  (error? ; invalid syntax
    (profile 1 2 3))
  (error? ; not a source object
    (profile 3))
  (begin
    (define str "(ugh (if \x3b2;))")
    (define bv (string->utf8 str))
    (define ip (open-bytevector-input-port bv))
    (define sfd (make-source-file-descriptor "foo" ip #t))
    #t)
  (eq? (eval `(profile ,(make-source-object sfd 2 3))) (void))
  (begin
    (define compile-triv-file
      (lambda (ifn ofn)
        (define insert-profile-forms
          (lambda (x)
            (unless (annotation? x) (errorf 'compile-triv-file "expected an annotation, got ~s" x))
            (let ([src (annotation-source x)] [exp (annotation-expression x)])
              `(begin (profile ,src)
                 ,(syntax-case exp ()
                    [(?do-times n e)
                      (eq? (annotation-expression #'?do-times) 'do-times)
                      (let ([n (annotation-expression #'n)])
                        `(do ([i ,n (fx- i 1)]) ((fx= i 0)) ,(insert-profile-forms #'e)))]
                    [(?print string)
                      (eq? (annotation-expression #'?print) 'print)
                      `(printf "~a\n" ,(annotation-expression #'string))]
                    [else (syntax-error exp)])))))
        (define parse
          (lambda (ifn)
            (let ([ip (open-file-input-port ifn)])
              (let ([sfd (make-source-file-descriptor ifn ip #t)])
                (let ([ip (transcoded-port ip (native-transcoder))])
                  (let f ([bfp 0])
                    (let-values ([(x bfp) (get-datum/annotations ip sfd bfp)])
                      (if (eof-object? x)
                          (begin (close-port ip) '())
                          (cons x (f bfp))))))))))
        (parameterize ([compile-profile 'source] [generate-profile-forms #f])
          (compile-to-file (list `(define (triv) ,@(map insert-profile-forms (parse ifn)))) ofn))))
    #t)
  (begin
    (with-output-to-file "testfile-triv.ss"
      (lambda ()
        (pretty-print '(do-times 10 (print "hello")))
        (pretty-print '(do-times 5 (print "goodbye"))))
      'replace)
    (compile-triv-file "testfile-triv.ss" "testfile-triv.so")
    (load "testfile-triv.so")
    #t)
  (equal?
    (with-output-to-string triv)
    "hello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\nhello\ngoodbye\ngoodbye\ngoodbye\ngoodbye\ngoodbye\n")
  (equal?
    (sort
      ; sort by bfp
     (lambda (x y) (< (list-ref x 2) (list-ref y 2)))
     ($just-file "testfile-triv.ss" (profile-dump-list)))
    '((1 "testfile-triv.ss" 0 29 1 1)
      (10 "testfile-triv.ss" 13 28 1 14)
      (1 "testfile-triv.ss" 30 60 2 1)
      (5 "testfile-triv.ss" 42 59 2 13)))
  (eqv? (profile-clear) (void))
)

(mat coverage
  (begin
    (mkfile "testfile.ss" '(printf "hello\n"))
    (define $ct0 (make-source-table))
    (define $ct0-src1
      (make-source-object
        (call-with-port (open-file-input-port "testfile.ss")
          (lambda (ip)
            (make-source-file-descriptor "testfile.ss" ip #t)))
        3 7))
    (define $ct0-src2
      (make-source-object
        (call-with-port (open-file-input-port "testfile.ss")
          (lambda (ip)
            (make-source-file-descriptor "testfile.ss" ip #t)))
        5 11))
    (define $ct0-src3
      (make-source-object
        (call-with-port (open-file-input-port "testfile.ss")
          (lambda (ip)
            (make-source-file-descriptor "not-testfile.ss" ip #t)))
        17 19))
    #t)
  (source-table? $ct0)
  (= (source-table-size $ct0) 0)
  (not (source-table-contains? $ct0 $ct0-src1))
  (eq? (source-table-ref $ct0 $ct0-src2 'q) 'q)
  (begin
    (source-table-set! $ct0 $ct0-src1 17)
    #t)
  (= (source-table-size $ct0) 1)
  (source-table-contains? $ct0 $ct0-src1)
  (not (source-table-contains? $ct0 $ct0-src2))
  (eq? (source-table-ref $ct0 $ct0-src3 'q) 'q)
  (begin
    (source-table-set! $ct0 $ct0-src2 37)
    (source-table-set! $ct0 $ct0-src3 43)
    #t)
  (= (source-table-size $ct0) 3)
  (source-table-contains? $ct0 $ct0-src1)
  (source-table-contains? $ct0 $ct0-src2)
  (source-table-contains? $ct0 $ct0-src3)
  (eqv? (source-table-ref $ct0 $ct0-src1 'q) 17)
  (eqv? (source-table-ref $ct0 $ct0-src2 'q) 37)
  (eqv? (source-table-ref $ct0 $ct0-src3 'q) 43)
  (let ([a (source-table-cell $ct0 $ct0-src1 #f)])
    (and (eqv? (cdr a) 17)
         (begin
           (set-cdr! a 23)
           #t)))
  (= (source-table-size $ct0) 3)
  (source-table-contains? $ct0 $ct0-src1)
  (source-table-contains? $ct0 $ct0-src2)
  (source-table-contains? $ct0 $ct0-src3)
  (eqv? (source-table-ref $ct0 $ct0-src1 'q) 23)
  (eqv? (source-table-ref $ct0 $ct0-src2 'q) 37)
  (eqv? (source-table-ref $ct0 $ct0-src3 'q) 43)
  (eqv? (source-table-delete! $ct0 $ct0-src1) (void))
  (= (source-table-size $ct0) 2)
  (not (source-table-contains? $ct0 $ct0-src1))
  (source-table-contains? $ct0 $ct0-src2)
  (source-table-contains? $ct0 $ct0-src3)
  (eqv? (source-table-delete! $ct0 $ct0-src3) (void))
  (= (source-table-size $ct0) 1)
  (not (source-table-contains? $ct0 $ct0-src1))
  (source-table-contains? $ct0 $ct0-src2)
  (not (source-table-contains? $ct0 $ct0-src3))
  (eqv? (source-table-delete! $ct0 $ct0-src2) (void))
  (= (source-table-size $ct0) 0)
  (not (source-table-contains? $ct0 $ct0-src1))
  (not (source-table-contains? $ct0 $ct0-src2))
  (not (source-table-contains? $ct0 $ct0-src3))
  (begin
    (define $source-table-filter
      (lambda (universe-ct ct)
        (let ([new-ct (make-source-table)])
          (for-each
            (lambda (p)
              (let ([src (car p)] [count (cdr p)])
                (when (source-table-contains? universe-ct src)
                  (source-table-set! new-ct src count))))
            (source-table-dump ct))
          new-ct)))
    (begin
      (mkfile "testfile-coverage1a.ss"
        '(library (testfile-coverage1a) (export a f) (import (chezscheme))
           (define-syntax a (lambda (x) #'(cons 0 1)))
           (define f (lambda (x) (if (= x 0) 1 (* x (f (- x 1))))))))
      (parameterize ([generate-covin-files #t] [compile-profile #t])
        (compile-library "testfile-coverage1a")))
    (begin
      (mkfile "testfile-coverage1b.ss"
        `(top-level-program
           (import (chezscheme) (testfile-coverage1a))
           (do ([i 3 (fx- i 1)])
               ((fx= i 0) (printf "~s\n" (f 3)))
             (printf "a = ~s\n" a))))
      (call-with-port (open-file-input-port "testfile-coverage1b.ss")
        (lambda (ip)
          (let ([sfd (make-source-file-descriptor "testfile-coverage1b.ss" ip #t)])
            (call-with-port (transcoded-port ip (native-transcoder))
              (lambda (ip)
                (call-with-port (open-file-output-port "testfile-coverage1b.so" (file-options replace))
                  (lambda (op)
                    (call-with-port (open-output-file "testfile-coverage1b.covin" 'replace)
                      (lambda (covop)
                        (parameterize ([compile-profile #t])
                          (compile-port ip op sfd #f covop))))))))))))
    (begin
      (mkfile "testfile-coverage1c.ss"
        '(top-level-program
           (import (chezscheme) (testfile-coverage1a))
           (do ([i 4 (fx- i 1)])
               ((fx= i 0) (printf "~s\n" (f 4)))
             (printf "a = ~s\n" a))))
      (call-with-port (open-file-input-port "testfile-coverage1c.ss")
        (lambda (ip)
          (let ([sfd (make-source-file-descriptor "testfile-coverage1c.ss" ip #t)])
            (call-with-port (transcoded-port ip (native-transcoder))
              (lambda (ip)
                (call-with-port (open-file-output-port "testfile-coverage1c.so" (file-options replace))
                  (lambda (op)
                    (call-with-port (open-output-file "testfile-coverage1c.covin" 'replace)
                      (lambda (covop)
                        (parameterize ([compile-profile #t])
                          (let-values ([(x fp) (get-datum/annotations ip sfd 0)])
                            (compile-to-port (list x) op sfd #f covop)))))))))))))
    (begin
      (mkfile "testfile-coverage1d.ss"
        '(import (chezscheme) (testfile-coverage1a))
        '(do ([i 3 (fx- i 1)])
             ((fx= i 0) (printf "~s\n" (f 5)))
           (printf "a = ~s\n" a)))
      (parameterize ([generate-covin-files #t] [compile-profile #t])
        (compile-program "testfile-coverage1d")))
    (define $ct0
      (let ()
        (define (with-source-input-port path p)
          (call-with-port
            (open-file-input-port path
              (file-options compressed)
              (buffer-mode block)
              (current-transcoder))
            p))
        (let ([ct (make-source-table)])
          (with-source-input-port "testfile-coverage1b.covin" (lambda (ip) (get-source-table! ip ct)))
          (with-source-input-port "testfile-coverage1c.covin" (lambda (ip) (get-source-table! ip ct (lambda (x y) (assert (= x y 0)) x))))
          ct)))
    #t)
  (source-table? $ct0)
  (andmap zero? (map cdr (source-table-dump $ct0)))
  (call-with-values
    (lambda ()
      (with-profile-tracker
        (lambda ()
          (call/cc
            (lambda (k)
              (values k
                (with-output-to-string
                  (lambda ()
                    (load-program "testfile-coverage1b.so")
                    (load-program "testfile-coverage1c.so")
                    (load-program "testfile-coverage1d.so")))))))))
    (lambda (ct k s)
      (let* ([ct ($source-table-filter $ct0 ct)])
        (if k
            (and (string=? s "a = (0 . 1)\na = (0 . 1)\na = (0 . 1)\n6\na = (0 . 1)\na = (0 . 1)\na = (0 . 1)\na = (0 . 1)\n24\na = (0 . 1)\na = (0 . 1)\na = (0 . 1)\n120\n")
                 (procedure? k)
                 (begin
                   (set! $ct1 ct)
                   (k #f "yup.")))
            (and (string=? s "yup.")
                 (begin
                   (set! $ct2 ct)
                   #t))))))
  (source-table? $ct1)
  (source-table? $ct2)
  (and
    (andmap
      (lambda (dumpit)
        (and (source-table-contains? $ct2 (car dumpit))
             (>= (source-table-ref $ct2 (car dumpit) #f) (cdr dumpit))))
      (source-table-dump $ct1))
    (andmap
      (lambda (dumpit)
        (and (source-table-contains? $ct1 (car dumpit))
             (<= (source-table-ref $ct1 (car dumpit) #f) (cdr dumpit))))
      (source-table-dump $ct2)))
  (not (ormap zero? (map cdr (source-table-dump $ct1))))
  (let ([dump (source-table-dump $ct1)])
    (define (file-found? path)
      (ormap
        (lambda (dumpit)
          (string=? (source-file-descriptor-path (source-object-sfd (car dumpit))) path))
        dump))
    (and (file-found? "testfile-coverage1a.ss")
         (file-found? "testfile-coverage1b.ss")
         (file-found? "testfile-coverage1c.ss")
         (not (file-found? "testfile-coverage1d.ss"))))
    (string=?
      (with-output-to-string
        (lambda ()
          ; shouldn't matter whether this is before or after the with-profile-tracker call
          (load-program "testfile-coverage1b.so")
          (let-values ([(ct . ignore) (with-profile-tracker #t
                                        (lambda ()
                                          (load-program "testfile-coverage1c.so")
                                          (load-program "testfile-coverage1d.so")))])
            (let ([ct ($source-table-filter $ct0 ct)])
              (set! $ct3 ct)))))
      "a = (0 . 1)\na = (0 . 1)\na = (0 . 1)\n6\na = (0 . 1)\na = (0 . 1)\na = (0 . 1)\na = (0 . 1)\n24\na = (0 . 1)\na = (0 . 1)\na = (0 . 1)\n120\n")
  (source-table? $ct3)
  (let ([dump (source-table-dump $ct3)])
    (define (file-found? path)
      (ormap
        (lambda (dumpit)
          (string=? (source-file-descriptor-path (source-object-sfd (car dumpit))) path))
        dump))
    (and (file-found? "testfile-coverage1a.ss")
         (file-found? "testfile-coverage1b.ss")
         (file-found? "testfile-coverage1c.ss")
         (not (file-found? "testfile-coverage1d.ss"))))
  ; the coverage table retrieved should include counts for both sets of load-program calls
  (and
    (andmap
      (lambda (dumpit)
        (>= (source-table-ref $ct3 (car dumpit) #f) (* 2 (cdr dumpit))))
      (source-table-dump $ct1))
    (andmap
      (lambda (dumpit)
        (<= (* 2 (source-table-ref $ct1 (car dumpit) #f)) (cdr dumpit)))
      (source-table-dump $ct3)))
  (begin
    (call-with-output-file "testfile.covout"
      (lambda (op)
        (put-source-table op $ct3))
      'replace)
    (define $ct5
      (let ([ct (make-source-table)])
        (call-with-input-file "testfile.covout" (lambda (ip) (get-source-table! ip ct)))
        ct))
    #t)
  (andmap
    (lambda (dumpit)
      (= (source-table-ref $ct5 (car dumpit) #f) (cdr dumpit)))
    (source-table-dump $ct3))
  (andmap
    (lambda (dumpit)
      (= (source-table-ref $ct3 (car dumpit) #f) (cdr dumpit)))
    (source-table-dump $ct5))
  (begin
    (call-with-input-file "testfile.covout" (lambda (ip) (get-source-table! ip $ct5 (lambda (x y) (- (* x y))))))
    #t)
  (andmap
    (lambda (dumpit)
      (= (source-table-ref $ct5 (car dumpit) #f) (- (expt (cdr dumpit) 2))))
    (source-table-dump $ct3))
)
