Change the errortrace protocol for `initialize-test-coverage-point' and

`test-covered' to use just the expression -- looks like there's no
reason to use an additional key.

Also, change its uses to map each syntax to an mcons where its mcar is
used to track coverage.  This is done everywhere, since it turns out to
be much faster to insert a `set-mcar!' with a 3d mpair, rather than a
call to a thunk.

Note that it still uses mpairs as a hack.  It "works" in the same way
that this simplified example does:

  (define-syntax m
    (let ([b (mcons 0 0)])
      (lambda (stx)
        (with-syntax ([b b])
          #'(case-lambda [() (mcar b)]
                         [(x) (set-mcar! b x)])))))

I think that it's fragile, and likely to stop working at some point, but
I don't see anything better for now.
This commit is contained in:
Eli Barzilay 2010-10-04 15:39:45 -04:00
parent ad76c9ea7b
commit 2189957b6f
7 changed files with 141 additions and 131 deletions

View File

@ -1246,10 +1246,12 @@
;; test coverage ;; test coverage
;; ;;
;; WARNING: much code copied from "collects/lang/htdp-langs.rkt"
(define test-coverage-enabled (make-parameter #t)) (define test-coverage-enabled (make-parameter #t))
(define current-test-coverage-info (make-thread-cell #f)) (define current-test-coverage-info (make-thread-cell #f))
(define (initialize-test-coverage-point key expr) (define (initialize-test-coverage-point expr)
(unless (thread-cell-ref current-test-coverage-info) (unless (thread-cell-ref current-test-coverage-info)
(let ([ht (make-hasheq)]) (let ([ht (make-hasheq)])
(thread-cell-set! current-test-coverage-info ht) (thread-cell-set! current-test-coverage-info ht)
@ -1272,15 +1274,19 @@
(send rep set-test-coverage-info ht on-sd off-sd #f))))))))) (send rep set-test-coverage-info ht on-sd off-sd #f)))))))))
(let ([ht (thread-cell-ref current-test-coverage-info)]) (let ([ht (thread-cell-ref current-test-coverage-info)])
(when ht (when ht
(hash-set! ht key (mcons #f expr))))) (hash-set! ht expr #;(box #f) (mcons #f #f)))))
(define (test-covered key) (define (test-covered expr)
(let ([ht (thread-cell-ref current-test-coverage-info)]) (let* ([ht (or (thread-cell-ref current-test-coverage-info)
(and ht (error 'deinprogramm-langs
(let ([v (hash-ref ht key)]) "internal-error: no test-coverage table"))]
(and v [v (hash-ref ht expr
(with-syntax ([v v]) (lambda ()
#'(set-mcar! v #t))))))) (error 'deinprogramm-langs
"internal-error: expression not found: ~.s"
expr)))])
#; (lambda () (set-box! v #t))
(with-syntax ([v v]) #'(#%plain-app set-mcar! v #t))))
(define-values/invoke-unit et:stacktrace@ (define-values/invoke-unit et:stacktrace@
(import et:stacktrace-imports^) (export (prefix et: et:stacktrace^))) (import et:stacktrace-imports^) (export (prefix et: et:stacktrace^)))

View File

@ -925,7 +925,7 @@ profile todo:
(define current-test-coverage-info (make-thread-cell #f)) (define current-test-coverage-info (make-thread-cell #f))
(define (initialize-test-coverage-point key expr) (define (initialize-test-coverage-point expr)
(unless (hash? (thread-cell-ref current-test-coverage-info)) (unless (hash? (thread-cell-ref current-test-coverage-info))
(let ([rep (drracket:rep:current-rep)]) (let ([rep (drracket:rep:current-rep)])
(when rep (when rep
@ -938,14 +938,14 @@ profile todo:
(when (hash? ht) (when (hash? ht)
;; if rep isn't around, we don't do test coverage... ;; if rep isn't around, we don't do test coverage...
;; this can happen when check syntax expands, for example ;; this can happen when check syntax expands, for example
(hash-set! ht key (mcons #f expr))))) (hash-set! ht expr #;(box #f) (mcons #f #f)))))
(define (test-covered key) (define (test-covered expr)
(let ([ht (thread-cell-ref current-test-coverage-info)]) (let ([ht (thread-cell-ref current-test-coverage-info)])
(and (hash? ht) ;; as in the `when' test in `initialize-test-coverage-point' (and (hash? ht) ;; as in the `when' test in `initialize-test-coverage-point'
(let ([v (hash-ref ht key #f)]) (let ([v (hash-ref ht expr #f)])
(and v ;; (and v (λ () (set-box! v #t)))
(λ () (set-mcar! v #t))))))) (and v (with-syntax ([v v]) #'(#%plain-app set-mcar! v #t)))))))
(define test-coverage-interactions-text<%> (define test-coverage-interactions-text<%>
(interface () (interface ()
@ -1075,7 +1075,6 @@ profile todo:
[locked-ht (make-hasheq)] [locked-ht (make-hasheq)]
[already-frozen-ht (make-hasheq)] [already-frozen-ht (make-hasheq)]
[actions-ht (make-hash)] [actions-ht (make-hash)]
[on/syntaxes (hash-map ht (λ (_ pr) pr))]
;; can-annotate : (listof (list boolean srcloc)) ;; can-annotate : (listof (list boolean srcloc))
;; boolean is #t => code was run ;; boolean is #t => code was run
@ -1083,8 +1082,8 @@ profile todo:
;; remove those that cannot be annotated ;; remove those that cannot be annotated
[can-annotate [can-annotate
(filter values (filter values
(map (λ (pr) (hash-map ht
(let ([stx (mcdr pr)]) (λ (stx covered?)
(and (syntax? stx) (and (syntax? stx)
(let ([src (syntax-source stx)] (let ([src (syntax-source stx)]
[pos (syntax-position stx)] [pos (syntax-position stx)]
@ -1092,8 +1091,8 @@ profile todo:
(and pos (and pos
span span
(send (get-defs) port-name-matches? src) (send (get-defs) port-name-matches? src)
(list (mcar pr) (make-srcloc (get-defs) #f #f pos span))))))) (list (mcar covered?)
on/syntaxes))] (make-srcloc (get-defs) #f #f pos span))))))))]
;; filtered : (listof (list boolean srcloc)) ;; filtered : (listof (list boolean srcloc))
;; remove redundant expressions ;; remove redundant expressions

View File

@ -66,7 +66,7 @@
;; expressions with test suite coverage information. Returning the ;; expressions with test suite coverage information. Returning the
;; first argument means no tests coverage information is collected. ;; first argument means no tests coverage information is collected.
;; test-coverage-point : syntax syntax -> (values syntax info) ;; test-coverage-point : syntax syntax integer -> (values syntax info)
;; sets a test coverage point for a single expression ;; sets a test coverage point for a single expression
(define (test-coverage-point body expr phase) (define (test-coverage-point body expr phase)
(if (and (test-coverage-enabled) (zero? phase)) (if (and (test-coverage-enabled) (zero? phase))
@ -240,10 +240,8 @@
(with-syntax ([key (datum->syntax #f key (quote-syntax here))] (with-syntax ([key (datum->syntax #f key (quote-syntax here))]
[expr expr] [expr expr]
[register-executed-once register-executed-once]);<- 3D! [register-executed-once register-executed-once]);<- 3D!
(syntax #'(begin (register-executed-once 'key)
(begin expr)))
(register-executed-once 'key)
expr))))
expr)) expr))
(define (get-execute-counts) (define (get-execute-counts)

View File

@ -418,27 +418,31 @@ be wrapped.}
Determines if the test coverage annotation is inserted into the code. Determines if the test coverage annotation is inserted into the code.
This parameter controls how compilation happens---it does not affect the This parameter controls how compilation happens---it does not affect the
dynamic behavior of the already compiled code. If the parameter is set, dynamic behavior of the already compiled code. If the parameter is set,
calls to @schemein[test-covered] are inserted into the code (and code generated by @schemein[test-covered] are inserted into the code (and
@schemein[initialize-test-coverage-point] is called during compilation). @schemein[initialize-test-coverage-point] is called during compilation).
If not, no calls to test-covered are inserted.} If not, no calls to @scheme[test-covered] code are inserted.}
@defproc[(test-covered (key any/c)) (or/c (-> void?) syntax? #f)]{ @defproc[(test-covered (stx any/c)) (or/c syntax? (-> void?) #f)]{
This is called during compilation of the program with a key value once This is called during compilation of the program with an expression for
for each point with the key for that program point that was passed to each point in the program that was passed to
@schemein[initialize-test-coverage-point]. @schemein[initialize-test-coverage-point].
If the result is @scheme[#f], this program point is not If the result is @scheme[#f], this program point is not instrumented. If
instrumented. If the result is syntax, it is inserted into the code, the result is syntax, it is inserted into the code, and if it is a
and if it is a thunk, the thunk is inserted into the code in an thunk, the thunk is inserted into the code in an application (using the
application. In either case, the syntax or the thunk should register thunk directly, as a 3D value). In either case, the syntax or the thunk
that the relevant point was covered.} should register that the relevant point was covered.
@defproc[(initialize-test-coverage-point (key any/c) (stx any/c)) void?]{ Note: using a thunk tends to be slow. Current uses in the Racket code
will create a mutable pair in @scheme[initialize-test-coverage-point],
and @scheme[test-covered] returns syntax that will set its mcar. (This
makes the resulting overhead about 3 times smaller.)}
@defproc[(initialize-test-coverage-point (stx any/c)) void?]{
During compilation of the program, this function is called with each During compilation of the program, this function is called with each
sub-expression of the program. The first argument is a special key sub-expression of the program. The argument is the syntax of this program
used to identify this program point. The second argument is the point, which is usually used as a key to identify this program point.}
syntax of this program point.}
@defthing[profile-key any/c]{ @defthing[profile-key any/c]{

View File

@ -85,11 +85,9 @@
(if (and (test-coverage-enabled) (if (and (test-coverage-enabled)
(zero? phase) (zero? phase)
(syntax-position expr)) (syntax-position expr))
(let* ([key (gensym 'test-coverage-point)]) (begin (initialize-test-coverage-point expr)
(initialize-test-coverage-point key expr) (let ([thunk (test-covered expr)])
(let ([thunk (test-covered key)]) (cond [(procedure? thunk)
(cond
[(procedure? thunk)
(with-syntax ([body body] (with-syntax ([body body]
[thunk thunk]) [thunk thunk])
#'(begin (#%plain-app thunk) body))] #'(begin (#%plain-app thunk) body))]
@ -97,8 +95,7 @@
(with-syntax ([body body] (with-syntax ([body body]
[thunk thunk]) [thunk thunk])
#'(begin thunk body))] #'(begin thunk body))]
[else [else body])))
body])))
body)) body))

View File

@ -1076,7 +1076,7 @@
(define test-coverage-enabled (make-parameter #t)) (define test-coverage-enabled (make-parameter #t))
(define current-test-coverage-info (make-thread-cell #f)) (define current-test-coverage-info (make-thread-cell #f))
(define (initialize-test-coverage-point key expr) (define (initialize-test-coverage-point expr)
(unless (thread-cell-ref current-test-coverage-info) (unless (thread-cell-ref current-test-coverage-info)
(let ([ht (make-hasheq)]) (let ([ht (make-hasheq)])
(thread-cell-set! current-test-coverage-info ht) (thread-cell-set! current-test-coverage-info ht)
@ -1144,16 +1144,19 @@
(send rep set-test-coverage-info ht on-sd off-sd #f))))))))) (send rep set-test-coverage-info ht on-sd off-sd #f)))))))))
(let ([ht (thread-cell-ref current-test-coverage-info)]) (let ([ht (thread-cell-ref current-test-coverage-info)])
(when ht (when ht
(hash-set! ht key (mcons #f expr))))) (hash-set! ht expr #;(box #f) (mcons #f #f)))))
(define (test-covered key) (define (test-covered expr)
(let* ([ht (thread-cell-ref current-test-coverage-info)] (let* ([ht (or (thread-cell-ref current-test-coverage-info)
[v (and ht (hash-ref ht key #f))]) (error 'htdp-langs
(with-syntax ([v v]) "internal-error: no test-coverage table"))]
#'(set-mcar! v #t)) [v (hash-ref ht expr
#; (lambda ()
(and v (error 'htdp-langs
(λ () (set-mcar! v #t))))) "internal-error: expression not found: ~.s"
expr)))])
#; (lambda () (set-box! v #t))
(with-syntax ([v v]) #'(#%plain-app set-mcar! v #t))))
(define-values/invoke-unit et:stacktrace@ (define-values/invoke-unit et:stacktrace@
(import et:stacktrace-imports^) (export (prefix et: et:stacktrace^))) (import et:stacktrace-imports^) (export (prefix et: et:stacktrace^)))

View File

@ -1,20 +1,23 @@
;; This file is is used in the context of sandboxed code, it uses the ;; This file is is used in the context of sandboxed code, it uses the
;; stacktrace interface from errortrace to find uncovered expressions. ;; stacktrace interface from errortrace to find uncovered expressions.
(module sandbox-coverage mzscheme #lang racket/base
(require errortrace/stacktrace mzlib/unit mzlib/list) (require errortrace/stacktrace racket/unit (for-template racket/base))
;; Test coverage run-time support ;; Test coverage run-time support
(define test-coverage-enabled (make-parameter #t)) (define test-coverage-enabled (make-parameter #t))
(define test-coverage-info (make-hash-table)) (define test-coverage-info (make-hasheq))
(define (initialize-test-coverage-point key expr) (define (initialize-test-coverage-point expr)
(hash-table-put! test-coverage-info key (mcons expr #f))) (hash-set! test-coverage-info expr (mcons #f #f)))
(define (test-covered key) (define (test-covered expr)
(let ([mpair (hash-table-get test-coverage-info key)]) (let ([v (hash-ref test-coverage-info expr
(λ () (set-mcdr! mpair #t)))) (lambda ()
(error 'sandbox-coverage
"internal error: no info for ~.s" expr)))])
(and v (with-syntax ([v v]) #'(#%plain-app set-mcar! v #t)))))
(define (get-uncovered-expressions) (define (get-uncovered-expressions)
(let* ([xs (hash-table-map test-coverage-info (lambda (k v) (let* ([xs (hash-map test-coverage-info
(cons (mcar v) (mcdr v))))] (lambda (k v) (cons k (mcar v))))]
[xs (filter (lambda (x) (syntax-position (car x))) xs)] [xs (filter (lambda (x) (syntax-position (car x))) xs)]
[xs (sort xs (lambda (x1 x2) [xs (sort xs (lambda (x1 x2)
(let ([p1 (syntax-position (car x1))] (let ([p1 (syntax-position (car x1))]
@ -62,9 +65,9 @@
(expand-syntax (if (syntax? e) (expand-syntax (if (syntax? e)
e e
(namespace-syntax-introduce (namespace-syntax-introduce
(datum->syntax-object #f e)))) (datum->syntax #f e))))
(namespace-base-phase)) (namespace-base-phase))
e) e)
immediate-eval?)))) immediate-eval?))))
(current-compile errortrace-compile-handler)) (current-compile errortrace-compile-handler)