;;; pdhtml.ss ;;; Copyright 1984-2017 Cisco Systems, Inc. ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. ;;; You may obtain a copy of the License at ;;; ;;; http://www.apache.org/licenses/LICENSE-2.0 ;;; ;;; Unless required by applicable law or agreed to in writing, software ;;; distributed under the License is distributed on an "AS IS" BASIS, ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;;; See the License for the specific language governing permissions and ;;; limitations under the License. ;;; NOTES: ;;; - fixed bug in define-tags: moved (void) end of text ... to start ;;; ;;; - to change palette to use white background with colorized text: ;;; (profile-palette ;;; (vector-map ;;; (lambda (p) (cons "white" (car p))) ;;; (profile-palette))) ;;; profile-dump-html suggestions from Oscar: ;;; ;;; We could probably build a table mapping source regions to procedure names ;;; in enough cases to actually be useful. If so, showing procedure name instead ;;; of line/char position would help the user get a high-level perspective on the ;;; profile results. Right now the user has to synthesize that perspective by ;;; remembering where each link led. ;;; ;;; Within the file view window, it would be nice to have a way to scan quickly ;;; through the hot spots within that file (we have some obscenely large source ;;; files at work). Perhaps you could reprise the profile spectrum horizontally ;;; in a short frame at the top of the window and rig it so that dragging, scroll ;;; wheel, or clicking on a color cycles through the regions tagged with that col> ;;; ;;; With a large range of profile counts to compress into a fairly small ;;; spectrum, it might be nice if there were a way to zoom into a range by ;;; clicking on the legend, either in the overview window or the file window. ;;; Reallocating the color map could be confusing with multiple windows open, ;;; but perhaps there's some javascript way to rig all the other colors to ;;; desaturate when you zoom into a range in one window. Perhaps intensity ;;; could be used to show the sub-ranges in varying shades of the main legend ;;; color. ;;; ;;; I notice that the profile annotations on the when expressions start at the te> ;;; expression rather than the start of the when. Yet the if expression annotati> ;;; starts at the beginning of the if expression and extends to the closing paren. ;;; Not sure if that made any sense, basically I'm trying to say that the "(when" ;;; itself (and closing paren) isn't colored the same as the test part. ;;; I don't remember exactly how we handled source annotations during wrapping and ;;; unwrapping, but it seems offhand that it might make sense to wrap the input ;;; source annotation around the transformer output so that the source info for t> ;;; when expression is transferred to the generated if expression. (begin (let () (include "types.ss") (module (make-tracker tracker-profile-ct) (define-record-type tracker (nongenerative) (fields profile-ct))) (define-record-type cc (nongenerative) (fields (mutable cookie) (mutable total) (mutable current) (mutable preceding))) (define-record-type (source-table $make-source-table $source-table?) (nongenerative) (sealed #t) (opaque #t) (fields ht) (protocol (lambda (new) (lambda () (define sfd-hash (lambda (sfd) (source-file-descriptor-crc sfd))) (define sfd=? (lambda (sfd1 sfd2) (and (fx= (source-file-descriptor-crc sfd1) (source-file-descriptor-crc sfd2)) (= (source-file-descriptor-length sfd1) (source-file-descriptor-length sfd2)) (string=? (source-file-descriptor-name sfd1) (source-file-descriptor-name sfd2))))) (new (make-hashtable sfd-hash sfd=?)))))) (define *local-profile-trackers* '()) (define op+ car) (define op- cdr) (define count+ (constant-case ptr-bits [(32) +] [(64) fx+])) (define count- (constant-case ptr-bits [(32) -] [(64) fx-])) (define count< (constant-case ptr-bits [(32) <] [(64) fx<])) (define get-counter-list (foreign-procedure "(cs)s_profile_counters" () ptr)) (define release-counters (foreign-procedure "(cs)s_profile_release_counters" () ptr)) (define rblock-count (lambda (rblock) (let sum ((op (rblock-op rblock))) (if (profile-counter? op) (profile-counter-count op) ; using #3%fold-left in case the #2% versions are profiled (#3%fold-left (lambda (a op) (count- a (sum op))) (#3%fold-left (lambda (a op) (count+ a (sum op))) 0 (op+ op)) (op- op)))))) (define profile-counts ; like profile-dump but returns ((count . (src ...)) ...) (case-lambda [() (profile-counts (get-counter-list))] [(counter*) ; disabling interrupts so we don't sum part of the counters for a block before ; an interrupt and the remaining counters after the interrupt, which can lead ; to inaccurate (and possibly negative) counts. we could disable interrupts just ; around the body of rblock-count to shorten the windows during which interrupts ; are disabled, but doing it here incurs less overhead (with-interrupts-disabled (fold-left (lambda (r x) (fold-left (lambda (r rblock) (cons (cons (rblock-count rblock) (rblock-srecs rblock)) r)) r (cdr x))) '() counter*))])) (define (snapshot who uncleared-count* cleared-count*) (lambda (tracker) (define cookie (cons 'vanilla 'wafer)) ; set current corresponding to each src to a total of its counts (let ([incr-current (lambda (count.src*) (let ([count (car count.src*)]) (for-each (lambda (src) (let ([a ($source-table-cell (tracker-profile-ct tracker) src #f)]) (when (count< count 0) (errorf who "negative profile count ~s for ~s" count src)) (let ([cc (cdr a)]) (if cc (if (eq? (cc-cookie cc) cookie) (cc-current-set! cc (count+ (cc-current cc) count)) (begin (cc-cookie-set! cc cookie) (cc-current-set! cc count))) (set-cdr! a (make-cc cookie 0 count 0)))))) (cdr count.src*))))]) (for-each incr-current uncleared-count*) (for-each incr-current cleared-count*)) ; then increment total of each affected cc by the delta between current and preceding (source-table-for-each (lambda (src cc) (when (eq? (cc-cookie cc) cookie) (let ([current (cc-current cc)]) (let ([delta (count- current (cc-preceding cc))]) (unless (eqv? delta 0) (when (count< delta 0) (errorf who "total profile count for ~s dropped from ~s to ~s" src (cc-preceding cc) current)) (cc-total-set! cc (count+ (cc-total cc) delta)) (cc-preceding-set! cc current)))))) (tracker-profile-ct tracker)) ; then reduce preceding by cleared counts (for-each (lambda (count.src*) (let ([count (car count.src*)]) (for-each (lambda (src) (let ([a ($source-table-cell (tracker-profile-ct tracker) src #f)]) (let ([cc (cdr a)]) (if cc (cc-preceding-set! cc (count- (cc-preceding cc) count)) (set-cdr! a (make-cc cookie 0 0 0)))))) (cdr count.src*)))) cleared-count*))) (define adjust-trackers! (lambda (who uncleared-counter* cleared-counter*) (let ([local-tracker* *local-profile-trackers*]) (unless (null? local-tracker*) (let ([uncleared-count* (profile-counts uncleared-counter*)] [cleared-count* (profile-counts cleared-counter*)]) (let ([snapshot (snapshot who uncleared-count* cleared-count*)]) (for-each snapshot local-tracker*))))))) (define $source-table-contains? (lambda (st src) (let ([src-ht (hashtable-ref (source-table-ht st) (source-sfd src) #f)]) (and src-ht (hashtable-contains? src-ht src))))) (define $source-table-ref (lambda (st src default) (let ([src-ht (hashtable-ref (source-table-ht st) (source-sfd src) #f)]) (if src-ht (hashtable-ref src-ht src default) default)))) (define $source-table-cell (lambda (st src default) (define same-sfd-src-hash (lambda (src) (source-bfp src))) (define same-sfd-src=? (lambda (src1 src2) (and (= (source-bfp src1) (source-bfp src2)) (= (source-efp src1) (source-efp src2))))) (let ([src-ht (let ([a (hashtable-cell (source-table-ht st) (source-sfd src) #f)]) (or (cdr a) (let ([src-ht (make-hashtable same-sfd-src-hash same-sfd-src=?)]) (set-cdr! a src-ht) src-ht)))]) (hashtable-cell src-ht src default)))) (define $source-table-delete! (lambda (st src) (let ([ht (source-table-ht st)] [sfd (source-sfd src)]) (let ([src-ht (hashtable-ref ht sfd #f)]) (when src-ht (hashtable-delete! src-ht src) (when (fx= (hashtable-size src-ht) 0) (hashtable-delete! ht sfd))))))) (define source-table-for-each (lambda (p st) (vector-for-each (lambda (src-ht) (let-values ([(vsrc vcount) (hashtable-entries src-ht)]) (vector-for-each p vsrc vcount))) (hashtable-values (source-table-ht st))))) (set-who! profile-clear (lambda () (define clear-links (lambda (op) (if (profile-counter? op) (profile-counter-count-set! op 0) (begin (for-each clear-links (op+ op)) (for-each clear-links (op- op)))))) (let ([counter* (get-counter-list)]) (adjust-trackers! who '() counter*) (for-each (lambda (x) (for-each (lambda (node) (clear-links (rblock-op node))) (cdr x))) counter*)))) (set-who! profile-release-counters (lambda () ; release-counters prunes out (and hands back) the released counters (let* ([dropped-counter* (release-counters)] [kept-counter* (get-counter-list)]) (adjust-trackers! who kept-counter* dropped-counter*)))) (set-who! profile-dump ; like profile-counts but returns ((src . count) ...), which requires more allocation ; profile-dump could use profile-counts but that would require even more allocation (lambda () ; could disable interrupts just around each call to rblock-count, but doing it here incurs less overhead (with-interrupts-disabled (fold-left (lambda (r x) (fold-left (lambda (r rblock) (let ([count (rblock-count rblock)]) (fold-left (lambda (r src) (cons (cons src count) r)) r (rblock-srecs rblock)))) r (cdr x))) '() (get-counter-list))))) (set-who! make-source-table (lambda () ($make-source-table))) (set-who! source-table? (lambda (x) ($source-table? x))) (set-who! source-table-size (lambda (st) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (let ([vsrc-ht (hashtable-values (source-table-ht st))]) (let ([n (vector-length vsrc-ht)]) (do ([i 0 (fx+ i 1)] [size 0 (fx+ size (hashtable-size (vector-ref vsrc-ht i)))]) ((fx= i n) size)))))) (set-who! source-table-contains? (lambda (st src) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (unless (source? src) ($oops who "~s is not a source object" src)) ($source-table-contains? st src))) (set-who! source-table-ref (lambda (st src default) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (unless (source? src) ($oops who "~s is not a source object" src)) ($source-table-ref st src default))) (set-who! source-table-set! (lambda (st src val) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (unless (source? src) ($oops who "~s is not a source object" src)) (set-cdr! ($source-table-cell st src #f) val))) (set-who! source-table-delete! (lambda (st src) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (unless (source? src) ($oops who "~s is not a source object" src)) ($source-table-delete! st src))) (set-who! source-table-cell (lambda (st src default) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (unless (source? src) ($oops who "~s is not a source object" src)) ($source-table-cell st src default))) (set-who! source-table-dump (lambda (st) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (let* ([vsrc-ht (hashtable-values (source-table-ht st))] [n (vector-length vsrc-ht)]) (do ([i 0 (fx+ i 1)] [dumpit* '() (let-values ([(vsrc vcount) (hashtable-entries (vector-ref vsrc-ht i))]) (let ([n (vector-length vsrc)]) (do ([i 0 (fx+ i 1)] [dumpit* dumpit* (cons (cons (vector-ref vsrc i) (vector-ref vcount i)) dumpit*)]) ((fx= i n) dumpit*))))]) ((fx= i n) dumpit*))))) (set-who! put-source-table (lambda (op st) (unless (and (output-port? op) (textual-port? op)) ($oops who "~s is not a textual output port" op)) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (fprintf op "(source-table") (let-values ([(vsfd vsrc-ht) (hashtable-entries (source-table-ht st))]) (vector-for-each (lambda (sfd src-ht) (let-values ([(vsrc vval) (hashtable-entries src-ht)]) (let ([n (vector-length vsrc)]) (unless (fx= n 0) (fprintf op "\n (file ~s ~s" (source-file-descriptor-name sfd) (source-file-descriptor-checksum sfd)) (let ([v (vector-sort (lambda (x1 x2) (< (vector-ref x1 0) (vector-ref x2 0))) (vector-map (lambda (src val) (vector (source-bfp src) (source-efp src) val)) vsrc vval))]) (let loop ([i 0] [last-bfp 0]) (unless (fx= i n) (let ([x (vector-ref v i)]) (let ([bfp (vector-ref x 0)] [efp (vector-ref x 1)] [val (vector-ref x 2)]) (let ([offset (- bfp last-bfp)] [len (- efp bfp)]) (fprintf op " (~s ~s ~s)" offset len val)) (loop (fx+ i 1) bfp)))))) (fprintf op ")"))))) vsfd vsrc-ht)) (fprintf op ")\n"))) (set-who! get-source-table! (rec get-source-table! (case-lambda [(ip st) (get-source-table! ip st #f)] [(ip st combine) (define (nnint? x) (and (integer? x) (exact? x) (nonnegative? x))) (define (token-oops what bfp) (if bfp ($oops who "expected ~a at file position ~s of ~s" what bfp ip) ($oops who "malformed source table reading from ~a" ip))) (define (next-token expected-type expected-value? what) (let-values ([(type val bfp efp) (read-token ip)]) (unless (and (eq? type expected-type) (expected-value? val)) (token-oops what bfp)) val)) (unless (and (input-port? ip) (textual-port? ip)) ($oops who "~s is not a textual input port" ip)) (unless ($source-table? st) ($oops who "~s is not a source table" st)) (unless (or (not combine) (procedure? combine)) ($oops who "~s is not a procedure" combine)) (next-token 'lparen not "open parenthesis") (next-token 'atomic (lambda (x) (eq? x 'source-table)) "identifier 'source-table'") (let file-loop () (let-values ([(type val bfp efp) (read-token ip)]) (unless (eq? type 'rparen) (unless (eq? type 'lparen) (token-oops "open parenthesis" bfp)) (next-token 'atomic (lambda (x) (eq? x 'file)) "identifier 'file'") (let* ([path (next-token 'atomic string? "string")] [checksum (next-token 'atomic nnint? "checksum")]) (let ([sfd (#%source-file-descriptor path checksum)]) (let entry-loop ([last-bfp 0]) (let-values ([(type val bfp efp) (read-token ip)]) (unless (eq? type 'rparen) (unless (eq? type 'lparen) (token-oops "open parenthesis" bfp)) (let* ([bfp (+ last-bfp (next-token 'atomic nnint? "file position"))] [efp (+ bfp (next-token 'atomic nnint? "file position"))] [val (get-datum ip)]) (next-token 'rparen not "close parenthesis") (let ([a ($source-table-cell st (make-source-object sfd bfp efp) #f)]) (set-cdr! a (if (and (cdr a) combine) (combine (cdr a) val) val))) (entry-loop bfp))))))) (file-loop))))]))) (set-who! with-profile-tracker (rec with-profile-tracker (case-lambda [(thunk) (with-profile-tracker #f thunk)] [(include-existing-counts? thunk) (define extract-covered-entries (lambda (profile-ct) (let ([covered-ct ($make-source-table)]) (source-table-for-each (lambda (src cc) (let ([count (cc-total cc)]) (unless (eqv? count 0) ($source-table-cell covered-ct src count)))) profile-ct) covered-ct))) (unless (procedure? thunk) ($oops who "~s is not a procedure" thunk)) (let* ([profile-ct ($make-source-table)] [tracker (make-tracker profile-ct)]) (unless include-existing-counts? ; set preceding corresponding to each src to a total of its dumpit counts ; set total to zero, since we don't want to count anything from before (for-each (lambda (count.src*) (let ([count (car count.src*)]) (for-each (lambda (src) (let ([a ($source-table-cell profile-ct src #f)]) (let ([cc (cdr a)]) (if cc (cc-preceding-set! cc (count+ (cc-preceding cc) count)) (set-cdr! a (make-cc #f 0 0 count)))))) (cdr count.src*)))) (profile-counts))) ; register for possible adjustment by profile-clear and profile-release-counters (let-values ([v* (fluid-let ([*local-profile-trackers* (cons tracker *local-profile-trackers*)]) (thunk))]) ; increment the recorded counts by the now current counts. ((snapshot who (profile-counts) '()) tracker) (apply values (extract-covered-entries profile-ct) v*)))])))) (let () (include "types.ss") (define check-dump (lambda (who x) (unless (and (list? x) (andmap (lambda (x) (and (pair? x) (source-object? (car x)) (let ([x (cdr x)]) (and (integer? x) (exact? x))))) x)) ($oops who "invalid dump ~s" x)))) (define-record-type filedata (fields (immutable sfd) (immutable ip) (mutable entry*) ; remaining fields are ignored by profile-dump-list (mutable max-count) (mutable ci) (mutable htmlpath) (mutable htmlfn) (mutable winid)) (nongenerative) (sealed #t) (protocol (lambda (new) (lambda (sfd ip) (new sfd ip '() #f #f #f #f #f))))) (define-record-type entrydata (fields (immutable fdata) (immutable bfp) (immutable efp) (mutable count) (mutable line) (mutable char) ; ci is ignored by profile-dump-list (mutable ci)) (nongenerative) (sealed #t) (protocol (lambda (new) (lambda (fdata bfp efp count) (new fdata bfp efp count #f #f #f))))) (define (gather-filedata who warn? dumpit*) ; returns list of fdata records, each holding a list of entries ; the entries are sorted based on their (unique) bfps (let ([fdata-ht (make-hashtable (lambda (x) (source-file-descriptor-crc x)) (lambda (x y) ; there's no way to make this foolproof, so we identify paths with ; same crc, length, and last component. this can cause problems ; only if two copies of the same file are loaded and used. (or (eq? x y) (and (= (source-file-descriptor-crc x) (source-file-descriptor-crc y)) (= (source-file-descriptor-length x) (source-file-descriptor-length y)) (string=? (path-last (source-file-descriptor-name x)) (path-last (source-file-descriptor-name y)))))))]) (define (open-source sfd) (cond [(hashtable-ref fdata-ht sfd #f)] [($open-source-file sfd) => (lambda (ip) (let ([fdata (make-filedata sfd ip)]) (hashtable-set! fdata-ht sfd fdata) fdata))] [else (when warn? (warningf who "unmodified source file ~s not found in source directories" (source-file-descriptor-name sfd))) (let ([fdata (make-filedata sfd #f)]) (hashtable-set! fdata-ht sfd fdata) fdata)])) (for-each (lambda (dumpit) (let ([source (car dumpit)]) (assert (source? source)) (let ([bfp (source-bfp source)]) (when (>= bfp 0) ; weed out block-profiling entries, whose bfps are negative (let ([fdata (open-source (source-sfd source))]) (filedata-entry*-set! fdata (cons (make-entrydata fdata bfp (source-efp source) (cdr dumpit)) (filedata-entry* fdata)))))))) dumpit*) (let ([fdatav (hashtable-values fdata-ht)]) (vector-for-each (lambda (fdata) (let ([entry* (sort (lambda (x y) (or (> (entrydata-bfp x) (entrydata-bfp y)) (and (= (entrydata-bfp x) (entrydata-bfp y)) (< (entrydata-efp x) (entrydata-efp y))))) (filedata-entry* fdata))]) #;(assert (not (null? entry*))) (let loop ([entry (car entry*)] [entry* (cdr entry*)] [new-entry* '()]) (if (null? entry*) (filedata-entry*-set! fdata (cons entry new-entry*)) (if (and (= (entrydata-bfp (car entry*)) (entrydata-bfp entry)) (= (entrydata-efp (car entry*)) (entrydata-efp entry))) (begin (entrydata-count-set! entry (+ (entrydata-count entry) (entrydata-count (car entry*)))) (loop entry (cdr entry*) new-entry*)) (loop (car entry*) (cdr entry*) (cons entry new-entry*))))))) fdatav) (vector->list fdatav)))) (let () (define (scan-file fdata) (let ([ip (filedata-ip fdata)] [line 1] [char 1]) (define (read-until bfp next) (let loop ([bfp bfp]) (unless (= bfp next) (cond [(eqv? (read-char ip) #\newline) (set! line (+ line 1)) (set! char 1)] [else (set! char (+ char 1))]) (loop (+ bfp 1))))) (let ([entry* (filedata-entry* fdata)]) ; already sorted by gather-filedata (let f ([bfp 0] [entry* entry*]) (unless (null? entry*) (let ([entry (car entry*)] [entry* (cdr entry*)]) (let ([next (entrydata-bfp entry)]) (read-until bfp next) (entrydata-line-set! entry line) (entrydata-char-set! entry char) (f next entry*)))))))) (set-who! profile-dump-list ; return list of lists of: ; - count ; - path ; current if line and char are not #f ; - bfp ; - efp ; - line ; may be #f ; - char ; may be #f (rec profile-dump-list (case-lambda [() (profile-dump-list #t)] [(warn?) (profile-dump-list warn? (profile-dump))] [(warn? dumpit*) (check-dump who dumpit*) (let ([fdata* (gather-filedata who warn? dumpit*)]) (for-each scan-file (remp (lambda (x) (not (filedata-ip x))) fdata*)) (let ([ls (map (lambda (entry) (let ([fdata (entrydata-fdata entry)]) (list (entrydata-count entry) (cond [(filedata-ip fdata) => port-name] [else (source-file-descriptor-name (filedata-sfd fdata))]) (entrydata-bfp entry) (entrydata-efp entry) (entrydata-line entry) (entrydata-char entry)))) (sort (lambda (x y) (> (entrydata-count x) (entrydata-count y))) (apply append (map filedata-entry* fdata*))))]) (for-each (lambda (fdata) (cond [(filedata-ip fdata) => close-input-port])) fdata*) ls))])))) (let () (define-record-type profilit (nongenerative #{profilit iw9f7z5ovg4jjetsvw5m0-2}) (sealed #t) (fields sfd bfp efp weight)) (define make-profile-database (lambda () (make-hashtable source-file-descriptor-crc (lambda (x y) (or (eq? x y) (and (= (source-file-descriptor-crc x) (source-file-descriptor-crc y)) (= (source-file-descriptor-length x) (source-file-descriptor-length y)) (string=? (path-last (source-file-descriptor-name x)) (path-last (source-file-descriptor-name y))))))))) (define profile-database #f) (define profile-source-data? #f) (define profile-block-data? #f) (define update-sfd! (lambda (cell sfd) ; if the recorded sfd is the same but not eq, it's likely from an earlier session. ; overwrite so remaining hashtable equality-procedure checks are more likely to ; succeed at the eq? check (unless (eq? (car cell) sfd) (set-car! cell sfd)))) (set-who! profile-clear-database (lambda () (set! profile-database #f))) (set-who! profile-dump-data (rec profile-dump-data (case-lambda [(ofn) (profile-dump-data ofn (profile-dump))] [(ofn dumpit*) (check-dump who dumpit*) (let ([op ($open-file-output-port who ofn (file-options replace))]) (on-reset (delete-file ofn #f) (on-reset (close-port op) (let* ([dump dumpit*] [max-count (inexact (fold-left max 1 (map cdr dump)))]) (for-each (lambda (dumpit) (let ([source (car dumpit)] [count (cdr dumpit)]) (fasl-write (make-profilit (source-sfd source) (source-bfp source) (source-efp source) ; compute weight as % of max count (fl/ (inexact count) max-count)) op))) dump))) (close-port op)))]))) (set! $profile-source-data? (lambda () profile-source-data?)) (set! $profile-block-data? (lambda () profile-block-data?)) (set-who! profile-load-data (lambda ifn* (define populate! (lambda (x) (unless (profilit? x) ($oops who "invalid profile data element ~s" x)) (unless profile-database (set! profile-database (make-profile-database))) (let ([ht (let* ([sfd (profilit-sfd x)] [cell (hashtable-cell profile-database sfd #f)]) (update-sfd! cell sfd) (or (cdr cell) (let ([ht (make-hashtable values =)]) (set-cdr! cell ht) ht)))]) ; each ht entry is an alist mapping efp -> (weight . n) where n is ; the number of contributing entries so far for this sfd, bfp, and efp. ; n is used to compute the average weight of the contributing entries. (let ([bfp.alist (hashtable-cell ht (profilit-bfp x) '())]) (cond [(assv (profilit-efp x) (cdr bfp.alist)) => (lambda (a) (let ([weight.n (cdr a)]) (let ([weight (car weight.n)] [n (cdr weight.n)]) (let ([new-n (fl+ n 1.0)]) (set-car! weight.n (fl/ (fl+ (* weight n) (profilit-weight x)) new-n)) (set-cdr! weight.n new-n)))))] [else (set-cdr! bfp.alist (cons (cons* (profilit-efp x) (profilit-weight x) 1.0) (cdr bfp.alist)))]))) (if (fxnegative? (profilit-bfp x)) (set! profile-block-data? #t) (set! profile-source-data? #t)))) (define (load-file ifn) (let ([ip ($open-file-input-port who ifn)]) (on-reset (close-port ip) (let f () (let ([x (fasl-read ip)]) (unless (eof-object? x) (with-tc-mutex (populate! x)) (f))))) (close-port ip))) (for-each (lambda (ifn) (unless (string? ifn) ($oops who "~s is not a string" ifn))) ifn*) (for-each load-file ifn*))) (set! $profile-show-database (lambda () (when profile-database (let-values ([(sfd* ht*) (hashtable-entries profile-database)]) (vector-for-each (lambda (sfd ht) (printf "~a:\n" (source-file-descriptor-name sfd)) (let-values ([(bfp* alist*) (hashtable-entries ht)]) (vector-for-each (lambda (bfp alist) (for-each (lambda (a) (printf " ~s, ~s: ~s\n" bfp (car a) (cadr a))) alist)) bfp* alist*))) sfd* ht*))))) (set! profile-query-weight (lambda (x) (define src->weight (lambda (src) (cond [(and profile-database (let* ([sfd (source-object-sfd src)] [ht (hashtable-ref profile-database sfd #f)]) (and ht (begin ; could do just one lookup if we had a nondestructive variant of ; hashtable-cell to call above (update-sfd! (hashtable-cell profile-database sfd #f) sfd) ht)))) => (lambda (ht) (let ([alist (hashtable-ref ht (source-object-bfp src) '())]) (cond [(assv (source-object-efp src) alist) => cadr] [(and (fxnegative? (source-object-bfp src)) (not (null? alist))) ($oops #f "block-profiling info is out-of-date for ~s" (source-file-descriptor-name (source-object-sfd src)))] ; no info for given bfp, efp...assume dead code and return 0 [else 0.0])))] ; no info for given sfd...assume not profiled and return #f [else #f]))) (if (source? x) (src->weight x) (let ([x (syntax->annotation x)]) (if (annotation? x) (src->weight (annotation-source x)) #f)))))) (let () ;;; The following copyright notice goes with the %html module. ;;; Copyright (c) 2005 R. Kent Dybvig ;;; Permission is hereby granted, free of charge, to any person obtaining a ;;; copy of this software and associated documentation files (the "Software"), ;;; to deal in the Software without restriction, including without limitation ;;; the rights to use, copy, modify, merge, publish, distribute, sublicense, ;;; and/or sell copies of the Software, and to permit persons to whom the ;;; Software is furnished to do so, subject to the following conditions: ;;; The above copyright notice and this permission notice shall be included in ;;; all copies or substantial portions of the Software. ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;;; DEALINGS IN THE SOFTWARE. (module %html (( <*> attribute $tag) ( <*> attribute $tag) ( <*> attribute $tag) (