You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

1534 lines
58 KiB
Scheme

;;; 7.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.
;;; system operations
(begin
(define scheme-start
(make-parameter
(lambda fns (for-each load fns) (new-cafe))
(lambda (p)
(unless (procedure? p)
($oops 'scheme-start "~s is not a procedure" p))
p)))
(define scheme-script
(make-parameter
(lambda (fn . fns)
(command-line (cons fn fns))
(command-line-arguments fns)
(load fn))
(lambda (p)
(unless (procedure? p)
($oops 'scheme-script "~s is not a procedure" p))
p)))
(define scheme-program
(make-parameter
(lambda (fn . fns)
(command-line (cons fn fns))
(command-line-arguments fns)
(load-program fn))
(lambda (p)
(unless (procedure? p)
($oops 'scheme-program "~s is not a procedure" p))
p)))
(define command-line-arguments
(make-parameter
'()
(lambda (x)
(unless (and (list? x) (andmap string? x))
($oops 'command-line-arguments "~s is not a list of strings" x))
x)))
(define command-line
(make-parameter
'("")
(lambda (x)
(unless (and (list? x) (not (null? x)) (andmap string? x))
($oops 'command-line "~s is not a nonempty list of strings" x))
x)))
(define-who #(r6rs: command-line)
(lambda ()
(#2%command-line)))
(define-who bytes-allocated
(let ([ba (foreign-procedure "(cs)bytes_allocated"
(scheme-object scheme-object)
scheme-object)])
(define filter-generation
(lambda (g)
(cond
[(and (fixnum? g) (fx<= 0 g (collect-maximum-generation))) g]
[(eq? g 'static) (constant static-generation)]
[else ($oops who "invalid generation ~s" g)])))
(define filter-space
(lambda (s)
(cond
[(assq s (constant real-space-alist)) => cdr]
[else ($oops who "invalid space ~s" s)])))
(case-lambda
[() (ba -1 -1)]
[(g) (ba (filter-generation g) -1)]
[(g s) (ba (if g (filter-generation g) -1) (if s (filter-space s) -1))])))
(define $spaces (lambda () (map car (constant real-space-alist))))
(define current-memory-bytes (foreign-procedure "(cs)curmembytes" () uptr))
(define maximum-memory-bytes (foreign-procedure "(cs)maxmembytes" () uptr))
(define reset-maximum-memory-bytes! (foreign-procedure "(cs)resetmaxmembytes" () void))
(define-who with-source-path
(lambda (whoarg fn p)
(unless (or (eq? whoarg #f) (string? whoarg) (symbol? whoarg)) ($oops who "invalid who argument ~s" whoarg))
(unless (string? fn) ($oops who "~s is not a string" fn))
(unless (procedure? p) ($oops who "~s is not a procedure" p))
(let ([dirs (source-directories)])
(if (or (equal? dirs '("")) (equal? dirs '(".")) ($fixed-path? fn))
(p fn)
(let loop ([ls dirs])
(if (null? ls)
($oops whoarg "file ~s not found in source directories" fn)
(let ([path (let ([dir (car ls)])
(if (or (string=? dir "") (string=? dir "."))
fn
(format
(if (directory-separator?
(string-ref dir
(fx- (string-length dir) 1)))
"~a~a"
"~a/~a")
dir fn)))])
(if (guard (c [#t #f]) (close-input-port (open-input-file path)) #t)
(p path)
(loop (cdr ls))))))))))
(set! $compressed-warning
(let ([warned? #f])
(lambda (who p)
(unless warned?
(set! warned? #t)
(warningf who "fasl file content is compressed internally; compressing the file (~s) is redundant and can slow fasl writing and reading significantly" p)))))
(set-who! fasl-read
(let ()
(define $fasl-read (foreign-procedure "(cs)fasl_read" (int fixnum ptr) ptr))
(define $bv-fasl-read (foreign-procedure "(cs)bv_fasl_read" (ptr ptr) ptr))
(define (get-uptr p)
(let ([k (get-u8 p)])
(let f ([k k] [n (fxsrl k 1)])
(if (fxlogbit? 0 k)
(let ([k (get-u8 p)])
(f k (logor (ash n 7) (fxsrl k 1))))
n))))
(define (get-uptr/bytes p)
(let ([k (get-u8 p)])
(let f ([k k] [n (fxsrl k 1)] [bytes 1])
(if (fxlogbit? 0 k)
(let ([k (get-u8 p)])
(f k (logor (ash n 7) (fxsrl k 1)) (fx+ bytes 1)))
(values n bytes)))))
(define (malformed p what) ($oops who "malformed fasl-object found in ~s (~a)" p what))
(define (check-header p)
(let ([bv (make-bytevector 8 (constant fasl-type-header))])
(unless (and (eqv? (get-bytevector-n! p bv 1 7) 7)
(bytevector=? bv (constant fasl-header)))
(malformed p "invalid header")))
(let ([n (get-uptr p)])
(unless (= n (constant scheme-version))
($oops who "incompatible fasl-object version ~a found in ~s"
($format-scheme-version n) p)))
(let ([n (get-uptr p)])
(unless (or (= n (constant machine-type-any)) (= n (constant machine-type)))
(cond
[(assv n (constant machine-type-alist)) =>
(lambda (a)
($oops who "incompatible fasl-object machine-type ~s found in ~s"
(cdr a) p))]
[else (malformed p "unrecognized machine type")])))
(unless (and (eqv? (get-u8 p) (char->integer #\()) ;)
(let f ()
(let ([n (get-u8 p)])
(and (not (eof-object? n)) ;(
(or (eqv? n (char->integer #\))) (f))))))
(malformed p "invalid list of base boot files")))
(define (go p situation)
(define (go1)
(if (and ($port-flags-set? p (constant port-flag-file))
(or (not ($port-flags-set? p (constant port-flag-compressed)))
(begin ($compressed-warning who p) #f))
(eqv? (binary-port-input-count p) 0))
($fasl-read ($port-info p) situation (port-name p))
(let fasl-entry ()
(let ([ty (get-u8 p)])
(cond
[(eof-object? ty) ty]
[(eqv? ty (constant fasl-type-header))
(check-header p)
(fasl-entry)]
[(eqv? ty (constant fasl-type-visit))
(go2 (eqv? situation (constant fasl-type-revisit)))]
[(eqv? ty (constant fasl-type-revisit))
(go2 (eqv? situation (constant fasl-type-visit)))]
[(eqv? ty (constant fasl-type-visit-revisit))
(go2 #f)]
[else (malformed p "invalid situation")])))))
(define (go2 skip?)
(let ([n (get-uptr p)])
(if skip?
(begin
(if (and (port-has-port-position? p) (port-has-set-port-position!? p))
(set-port-position! p (+ (port-position p) n))
(get-bytevector-n p n))
(go1))
(let ([compressed-flag (get-u8 p)])
(cond
[(or (eqv? compressed-flag (constant fasl-type-gzip)) (eqv? compressed-flag (constant fasl-type-lz4)))
(let-values ([(dest-size dest-size-bytes) (get-uptr/bytes p)])
(let* ([src-size (- n 1 dest-size-bytes)]
[bv (get-bytevector-n p src-size)]
[bv ($bytevector-uncompress bv dest-size
(if (eqv? compressed-flag (constant fasl-type-gzip))
(constant COMPRESS-GZIP)
(constant COMPRESS-LZ4)))])
($bv-fasl-read bv (port-name p))))]
[(eqv? compressed-flag (constant fasl-type-uncompressed))
($bv-fasl-read (get-bytevector-n p (- n 1)) (port-name p))]
[else (malformed p "invalid compression")])))))
(unless (and (input-port? p) (binary-port? p))
($oops who "~s is not a binary input port" p))
(go1))
(case-lambda
[(p) (go p (constant fasl-type-visit-revisit))]
[(p situation)
(go p
(case situation
[(visit) (constant fasl-type-visit)]
[(revisit) (constant fasl-type-revisit)]
[(load) (constant fasl-type-visit-revisit)]
[else ($oops who "invalid situation ~s" situation)]))])))
(define ($compiled-file-header? ip)
(let ([pos (port-position ip)])
(let ([cfh? (let* ([bv (constant fasl-header)] [n (bytevector-length bv)])
(let f ([i 0])
(or (fx= i n)
(and (eqv? (get-u8 ip) (bytevector-u8-ref bv i))
(f (fx+ i 1))))))])
(set-port-position! ip pos)
cfh?)))
(let ()
(define do-load-binary
(lambda (who fn ip situation for-import? importer)
(let ([load-binary (make-load-binary who fn situation for-import? importer)])
(let ([x (fasl-read ip situation)])
(unless (eof-object? x)
(let loop ([x x])
(let ([next-x (fasl-read ip situation)])
(if (eof-object? next-x)
(load-binary x)
(begin (load-binary x) (loop next-x))))))))))
(define (make-load-binary who fn situation for-import? importer)
(module (Lexpand? recompile-info? library/ct-info? library/rt-info? program-info?)
(import (nanopass))
(include "base-lang.ss")
(include "expand-lang.ss"))
(lambda (x)
(cond
[(procedure? x) (x)]
[(library/rt-info? x) ($install-library/rt-desc x for-import? importer fn)]
[(library/ct-info? x) ($install-library/ct-desc x for-import? importer fn)]
[(program-info? x) ($install-program-desc x)]
[(recompile-info? x) (void)]
[(Lexpand? x) ($interpret-backend x situation for-import? importer fn)]
; NB: this is here to support the #t inserted by compile-file-help2 after header information
[(eq? x #t) (void)]
[else ($oops who "unexpected value ~s read from ~a" x fn)])))
(define (do-load who fn situation for-import? importer ksrc)
(let ([ip ($open-file-input-port who fn)])
(on-reset (close-port ip)
(let ([fp (let ([start-pos (port-position ip)])
(if (and (eqv? (get-u8 ip) (char->integer #\#))
(eqv? (get-u8 ip) (char->integer #\!))
(let ([b (get-u8 ip)]) (or (eqv? b (char->integer #\space)) (eqv? b (char->integer #\/)))))
(let loop ([fp 3])
(let ([b (get-u8 ip)])
(if (eof-object? b)
fp
(let ([fp (+ fp 1)])
(if (eqv? b (char->integer #\newline))
fp
(loop fp))))))
(begin (set-port-position! ip start-pos) 0)))])
(if ($compiled-file-header? ip)
(begin
(do-load-binary who fn ip situation for-import? importer)
(close-port ip))
(begin
(unless ksrc
(close-port ip)
($oops who "~a is not a compiled file" fn))
(unless (eqv? fp 0) (set-port-position! ip 0))
(let ([sfd ($source-file-descriptor fn ip (eqv? fp 0))])
(unless (eqv? fp 0) (set-port-position! ip fp))
; whack ip so on-reset close-port call above closes the text port
(set! ip (transcoded-port ip (current-transcoder)))
(ksrc ip sfd ($make-read ip sfd fp)))))))))
(set! $make-load-binary
(lambda (fn)
(make-load-binary '$make-load-binary fn 'load #f #f)))
(set-who! load-compiled-from-port
(lambda (ip)
(unless (and (input-port? ip) (binary-port? ip))
($oops who "~s is not a binary input port" ip))
(do-load-binary who (port-name ip) ip 'load #f #f)))
(set-who! visit-compiled-from-port
(lambda (ip)
(unless (and (input-port? ip) (binary-port? ip))
($oops who "~s is not a binary input port" ip))
(do-load-binary who (port-name ip) ip 'visit #f #f)))
(set-who! revisit-compiled-from-port
(lambda (ip)
(unless (and (input-port? ip) (binary-port? ip))
($oops who "~s is not a binary input port" ip))
(do-load-binary who (port-name ip) ip 'revisit #f #f)))
(set-who! load-program
(rec load-program
(case-lambda
[(fn) (load-program fn eval)]
[(fn ev)
(unless (string? fn) ($oops who "~s is not a string" fn))
(unless (procedure? ev) ($oops who "~s is not a procedure" ev))
(with-source-path who fn
(lambda (fn)
(do-load who fn 'load #f #f
(lambda (ip sfd do-read)
($set-port-flags! ip (constant port-flag-r6rs))
(let loop ([x* '()])
(let ([x (do-read)])
(if (eof-object? x)
(begin
(close-port ip)
(ev `(top-level-program ,@(reverse x*)))
(void))
(loop (cons x x*)))))))))])))
(set-who! load-library ; like load, but sets #!r6rs mode
(rec load-library
(case-lambda
[(fn) (load-library fn eval)]
[(fn ev)
(unless (string? fn) ($oops who "~s is not a string" fn))
(unless (procedure? ev) ($oops who "~s is not a procedure" ev))
(with-source-path who fn
(lambda (fn)
(do-load who fn 'load #f #f
(lambda (ip sfd do-read)
($set-port-flags! ip (constant port-flag-r6rs))
(let loop ()
(let ([x (do-read)])
(unless (eof-object? x)
(ev x)
(loop))))
(close-port ip)))))])))
(set! $load-library ; for syntax.ss load-library
; like load, but sets #!r6rs mode and does not use with-source-path,
; since syntax.ss load-library has already determined the path.
; adds fn's directory to source-directories
(lambda (fn situation importer)
(define who 'import)
(let ([fn (let ([host-fn (format "~a.~s" (path-root fn) (machine-type))])
(if (file-exists? host-fn) host-fn fn))])
(do-load who fn situation #t importer
(lambda (ip sfd do-read)
($set-port-flags! ip (constant port-flag-r6rs))
(parameterize ([source-directories (cons (path-parent fn) (source-directories))])
(let loop ()
(let ([x (do-read)])
(unless (eof-object? x)
(eval x)
(loop)))))
(close-port ip))))))
(set-who! load
(rec load
(case-lambda
[(fn) (load fn eval)]
[(fn ev)
(unless (string? fn) ($oops who "~s is not a string" fn))
(unless (procedure? ev) ($oops who "~s is not a procedure" ev))
(with-source-path who fn
(lambda (fn)
(do-load who fn 'load #f #f
(lambda (ip sfd do-read)
(let loop ()
(let ([x (do-read)])
(unless (eof-object? x)
(ev x)
(loop))))
(close-port ip)))))])))
(set! $visit
(lambda (who fn importer)
(do-load who fn 'visit #t importer #f)))
(set! $revisit
(lambda (who fn importer)
(do-load who fn 'revisit #t importer #f)))
(set-who! visit
(lambda (fn)
(do-load who fn 'visit #f #f #f)))
(set-who! revisit
(lambda (fn)
(do-load who fn 'revisit #f #f #f))))
(let ()
(module sstats-record (make-sstats sstats? sstats-cpu sstats-real
sstats-bytes sstats-gc-count sstats-gc-cpu
sstats-gc-real sstats-gc-bytes
set-sstats-cpu! set-sstats-real!
set-sstats-bytes! set-sstats-gc-count!
set-sstats-gc-cpu! set-sstats-gc-real!
set-sstats-gc-bytes!)
(define-record-type (sstats make-sstats sstats?)
(nongenerative #{sstats pfwch3jd8ts96giujpitoverj-0})
(sealed #t)
(fields
(mutable cpu sstats-cpu set-sstats-cpu!)
(mutable real sstats-real set-sstats-real!)
(mutable bytes sstats-bytes set-sstats-bytes!)
(mutable gc-count sstats-gc-count set-sstats-gc-count!)
(mutable gc-cpu sstats-gc-cpu set-sstats-gc-cpu!)
(mutable gc-real sstats-gc-real set-sstats-gc-real!)
(mutable gc-bytes sstats-gc-bytes set-sstats-gc-bytes!))
(protocol
(lambda (new)
(lambda (cpu real bytes gc-count gc-cpu gc-real gc-bytes)
(new cpu real bytes gc-count gc-cpu gc-real gc-bytes))))))
(define exact-integer? (lambda (x) (and (integer? x) (exact? x))))
(set-who! make-sstats
(lambda (cpu real bytes gc-count gc-cpu gc-real gc-bytes)
(define verify-time
(lambda (name x)
(unless (time? x)
($oops who "~s value ~s is not a time record" name x))))
(define verify-exact-integer
(lambda (name x)
(unless (exact-integer? x)
($oops who "~s value ~s is not an exact integer" name x))))
(import sstats-record)
(verify-time 'cpu cpu)
(verify-time 'real real)
(verify-exact-integer 'bytes bytes)
(verify-exact-integer 'gc-count gc-count)
(verify-time 'gc-cpu gc-cpu)
(verify-time 'gc-real gc-real)
(verify-exact-integer 'gc-bytes gc-bytes)
(make-sstats cpu real bytes gc-count gc-cpu gc-real gc-bytes)))
(set! sstats? (lambda (x) (import sstats-record) (sstats? x)))
(let ()
(define verify-sstats
(lambda (who x)
(import sstats-record)
(unless (sstats? x) ($oops who "~s is not an sstats record" x))))
(define verify-exact-integer
(lambda (who x)
(unless (exact-integer? x)
($oops who "~s is not an exact integer" x))))
(define verify-time
(lambda (who x)
(unless (time? x)
($oops who "~s is not a time record" x))))
(define-syntax field
(lambda (x)
(syntax-case x ()
[(_ name verify-arg)
(with-syntax ([sstats-name (construct-name #'sstats-record "sstats-" #'name)]
[set-sstats-name! (construct-name #'sstats-record "set-sstats-" #'name "!")])
#'(begin
(set-who! sstats-name
(lambda (x)
(import sstats-record)
(verify-sstats who x)
(sstats-name x)))
(set-who! set-sstats-name!
(lambda (x n)
(import sstats-record)
(verify-sstats who x)
(verify-arg who n)
(set-sstats-name! x n)))))])))
(field cpu verify-time)
(field real verify-time)
(field bytes verify-exact-integer)
(field gc-count verify-exact-integer)
(field gc-cpu verify-time)
(field gc-real verify-time)
(field gc-bytes verify-exact-integer)))
(define-who sstats-print
(rec sstats-print
(case-lambda
[(s) (sstats-print s (current-output-port))]
[(s port)
(unless (sstats? s)
($oops who "~s is not an sstats record" s))
(unless (and (output-port? port) (textual-port? port))
($oops who "~s is not a textual output port" port))
(let ([collections (sstats-gc-count s)]
[time->string
(lambda (x)
;; based on record-writer for ts in date.ss
(let ([sec (time-second x)] [nsec (time-nanosecond x)])
(if (and (< sec 0) (> nsec 0))
(format "-~d.~9,'0ds" (- -1 sec) (- 1000000000 nsec))
(format "~d.~9,'0ds" sec nsec))))])
(if (zero? collections)
(fprintf port
" no collections
~a elapsed cpu time
~a elapsed real time
~s bytes allocated
"
(time->string (sstats-cpu s))
(time->string (sstats-real s))
(sstats-bytes s))
(fprintf port
" ~s collection~:p
~a elapsed cpu time, including ~a collecting
~a elapsed real time, including ~a collecting
~s bytes allocated, including ~s bytes reclaimed
"
collections
(time->string (sstats-cpu s)) (time->string (sstats-gc-cpu s))
(time->string (sstats-real s)) (time->string (sstats-gc-real s))
(sstats-bytes s) (sstats-gc-bytes s))))])))
(define display-statistics
(case-lambda
[() (display-statistics (current-output-port))]
[(p)
(unless (and (output-port? p) (textual-port? p))
($oops 'display-statistics "~s is not a textual output port" p))
(sstats-print (statistics) p)]))
(define-who sstats-difference
(lambda (a b)
(unless (sstats? a)
($oops who "~s is not an sstats record" a))
(unless (sstats? b)
($oops who "~s is not an sstats record" b))
(let ([int-diff (lambda (f a b) (- (f a) (f b)))]
[time-diff (lambda (f a b) (time-difference (f a) (f b)))])
(make-sstats
(time-diff sstats-cpu a b)
(time-diff sstats-real a b)
(int-diff sstats-bytes a b)
(int-diff sstats-gc-count a b)
(time-diff sstats-gc-cpu a b)
(time-diff sstats-gc-real a b)
(int-diff sstats-gc-bytes a b)))))
(define collect-generation-radix
(make-parameter
4
(lambda (v)
(unless (and (fixnum? v) (fx< 0 v))
($oops 'collect-generation-radix "~s is not a positive fixnum" v))
v)))
(define $reset-protect
(lambda (body out)
((call/cc
(lambda (k)
(parameterize ([reset-handler
(lambda ()
(k (lambda ()
(out)
((reset-handler)))))])
(with-exception-handler
(lambda (c)
; would prefer not to burn bridges even for serious condition
; if the exception is continuable, but we have no way to know
; short of grubbing through the continuation
(if (serious-condition? c)
(k (lambda () (out) (raise c)))
(raise-continuable c)))
(lambda ()
(call-with-values body
(case-lambda
[(v) (lambda () v)]
[v* (lambda () (apply values v*))]))))))))))
(define exit-handler)
(define reset-handler)
(define abort-handler)
(let ([c-exit (foreign-procedure "(cs)c_exit" (integer-32) void)])
(define (integer-32? x)
(and (integer? x)
(exact? x)
(<= #x-80000000 x #x7fffffff)))
(set! exit-handler
($make-thread-parameter
(case-lambda
[() (c-exit 0)]
[(x . args) (c-exit (if (eqv? x (void)) 0 (if (integer-32? x) x -1)))])
(lambda (v)
(unless (procedure? v)
($oops 'exit-handler "~s is not a procedure" v))
v)))
(set! reset-handler
($make-thread-parameter
(lambda () (c-exit 0))
(lambda (v)
(unless (procedure? v)
($oops 'reset-handler "~s is not a procedure" v))
v)))
(set! abort-handler
($make-thread-parameter
(case-lambda
[() (c-exit -1)]
[(x) (c-exit (if (eqv? x (void)) 0 (if (integer-32? x) x -1)))])
(lambda (v)
(unless (procedure? v)
($oops 'abort-handler "~s is not a procedure" v))
v))))
(let ()
(define (unexpected-return who)
($oops who (format "unexpected return from ~s handler" who)))
(set-who! exit
(lambda args
(apply (exit-handler) args)
(unexpected-return who)))
(set-who! #(r6rs: exit)
(case-lambda
[() ((exit-handler)) (unexpected-return who)]
[(x) ((exit-handler) x) (unexpected-return who)]))
(set-who! reset
(lambda ()
((reset-handler))
(unexpected-return who)))
(set-who! abort
(case-lambda
[() ((abort-handler)) (unexpected-return who)]
[(x) ((abort-handler) x) (unexpected-return who)])))
(define $interrupt ($make-thread-parameter void))
(define $format-scheme-version
(lambda (n)
(if (= (logand n 255) 0)
(format "~d.~d"
(ash n -16)
(logand (ash n -8) 255))
(format "~d.~d.~d"
(ash n -16)
(logand (ash n -8) 255)
(logand n 255)))))
; set in back.ss
(define $scheme-version)
(define scheme-version-number
(lambda ()
(let ([n (constant scheme-version)])
(values
(ash n -16)
(logand (ash n -8) 255)
(logand n 255)))))
(define scheme-version
(let ([s #f])
(lambda ()
(unless s
(set! s
(format "~:[Petite ~;~]Chez Scheme Version ~a"
$compiler-is-loaded?
$scheme-version)))
s)))
(define petite?
(lambda ()
(not $compiler-is-loaded?)))
(define threaded?
(lambda ()
(if-feature pthreads #t #f)))
(define get-process-id (foreign-procedure "(cs)getpid" () integer-32))
(set! get-thread-id
(lambda ()
($tc-field 'threadno ($tc))))
(define-who sleep
(let ([fp (foreign-procedure "(cs)nanosleep" (ptr ptr) void)])
(lambda (t)
(unless (and (time? t) (eq? (time-type t) 'time-duration))
($oops who "~s is not a time record of type time-duration" t))
(let ([s (time-second t)])
(when (>= s 0)
(fp s (time-nanosecond t)))))))
(define $scheme-greeting
(lambda ()
(format "~a\nCopyright 1984-2022 Cisco Systems, Inc.\n"
(scheme-version))))
(define $session-key #f)
(define $scheme-init)
(define $scheme)
(define $script)
(define $as-time-goes-by)
(define collect)
(define break-handler)
(define debug)
(let ()
(define debug-condition* '())
(module (docollect collect-init)
(define gc-trip 0)
(define gc-cpu (make-time 'time-collector-cpu 0 0))
(define gc-real (make-time 'time-collector-real 0 0))
(define gc-bytes 0)
(define gc-count 0)
(define start-bytes 0)
(define docollect
(let ([do-gc (foreign-procedure "(cs)do_gc" (int int int) void)])
(lambda (p)
(with-tc-mutex
(unless (= $active-threads 1)
($oops 'collect "cannot collect when multiple threads are active"))
(let-values ([(trip g gmintarget gmaxtarget) (p gc-trip)])
(set! gc-trip trip)
(let ([cpu (current-time 'time-thread)] [real (current-time 'time-monotonic)])
(set! gc-bytes (+ gc-bytes (bytes-allocated)))
(when (collect-notify)
(fprintf (console-output-port)
"~%[collecting generation ~s into generation ~s..."
g gmaxtarget)
(flush-output-port (console-output-port)))
(when (eqv? g (collect-maximum-generation))
($clear-source-lines-cache))
(do-gc g gmintarget gmaxtarget)
($close-resurrected-files)
(when-feature pthreads
($close-resurrected-mutexes&conditions))
(when (collect-notify)
(fprintf (console-output-port) "done]~%")
(flush-output-port (console-output-port)))
(set! gc-bytes (- gc-bytes (bytes-allocated)))
(set! gc-cpu (add-duration gc-cpu (time-difference (current-time 'time-thread) cpu)))
(set! gc-real (add-duration gc-real (time-difference (current-time 'time-monotonic) real)))
(set! gc-count (1+ gc-count))))))))
(define collect-init
(lambda ()
(set! gc-trip 0)
(set! gc-cpu (make-time 'time-collector-cpu 0 0))
(set! gc-real (make-time 'time-collector-real 0 0))
(set! gc-count 0)
(set! gc-bytes 0)
(set! start-bytes (bytes-allocated))))
(set! $gc-real-time (lambda () gc-real))
(set! $gc-cpu-time (lambda () gc-cpu))
(set! initial-bytes-allocated (lambda () start-bytes))
(set! bytes-deallocated (lambda () gc-bytes))
(set! collections (lambda () gc-count))
(set! statistics
(lambda ()
(make-sstats
(current-time 'time-thread)
(current-time 'time-monotonic)
(+ (- (bytes-allocated) start-bytes) gc-bytes)
gc-count
gc-cpu
gc-real
gc-bytes))))
(set-who! collect
(let ()
(define collect0
(lambda ()
(docollect
(lambda (gct)
(let ([gct (+ gct 1)])
(let ([cmg (collect-maximum-generation)])
(let loop ([g cmg])
(if (= (modulo gct (expt (collect-generation-radix) g)) 0)
(if (fx= g cmg)
(values 0 g (fxmin g 1) g)
(values gct g 1 (fx+ g 1)))
(loop (fx- g 1))))))))))
(define collect2
(lambda (g gmintarget gmaxtarget)
(docollect
(lambda (gct)
(values
; make gc-trip to look like we've just collected generation g
; w/o also having collected generation g+1
(if (fx= g (collect-maximum-generation))
0
(let ([gct (+ gct 1)])
(define (trip g)
(let ([n (expt (collect-generation-radix) g)])
(+ gct (modulo (- n gct) n))))
(let ([next (trip g)] [limit (trip (fx+ g 1))])
(if (< next limit) next (- limit 1)))))
g gmintarget gmaxtarget)))))
(case-lambda
[() (collect0)]
[(g)
(let ([cmg (collect-maximum-generation)])
(unless (and (fixnum? g) (fx<= 0 g cmg))
($oops who "invalid generation ~s" g))
(let ([gtarget (if (fx= g cmg) g (fx+ g 1))])
(collect2 g gtarget gtarget)))]
[(g gtarget)
(let ([cmg (collect-maximum-generation)])
(unless (and (fixnum? g) (fx<= 0 g cmg))
($oops who "invalid generation ~s" g))
(unless (if (fx= g cmg)
(or (eqv? gtarget g) (eq? gtarget 'static))
(or (eqv? gtarget g) (eqv? gtarget (fx+ g 1))))
($oops who "invalid target generation ~s for generation ~s" gtarget g)))
(let ([gtarget (if (eq? gtarget 'static) (constant static-generation) gtarget)])
(collect2 g gtarget gtarget))]
[(g gmintarget gmaxtarget)
(let ([cmg (collect-maximum-generation)])
(unless (and (fixnum? g) (fx<= 0 g cmg))
($oops who "invalid generation ~s" g))
(unless (if (fx= g cmg)
(or (eqv? gmaxtarget g) (eq? gmaxtarget 'static))
(or (eqv? gmaxtarget g) (eqv? gmaxtarget (fx+ g 1))))
($oops who "invalid maximum target generation ~s for generation ~s" gmaxtarget g))
(unless (or (eqv? gmintarget gmaxtarget)
(and (fixnum? gmintarget)
(fx<= 1 gmintarget (if (fixnum? gmaxtarget) gmaxtarget cmg))))
($oops who "invalid minimum target generation ~s for generation ~s and maximum target generation ~s" gmintarget g gmaxtarget)))
(collect2 g
(if (eq? gmintarget 'static) (constant static-generation) gmintarget)
(if (eq? gmaxtarget 'static) (constant static-generation) gmaxtarget))])))
(set! collect-rendezvous
(let ([fire-collector (foreign-procedure "(cs)fire_collector" () void)])
(lambda ()
(fire-collector)
($collect-rendezvous))))
(set! keyboard-interrupt-handler
($make-thread-parameter
(lambda ()
(clear-output-port (console-output-port))
(fresh-line (console-output-port))
(flush-output-port (console-output-port))
(($interrupt)))
(lambda (x)
(unless (procedure? x)
($oops 'keyboard-interrupt-handler "~s is not a procedure" x))
x)))
(let ()
(define register-scheme-signal
(foreign-procedure "(cs)register_scheme_signal" (iptr) void))
(define signal-alist '())
(set! register-signal-handler
(lambda (sig handler)
(unless (fixnum? sig)
($oops 'register-signal-handler "~s is not a fixnum" sig))
(unless (procedure? handler)
($oops 'register-signal-handler "~s is not a procedure" handler))
(critical-section
(register-scheme-signal sig)
(let ((a (assq sig signal-alist)))
(if a
(set-cdr! a handler)
(set! signal-alist (cons (cons sig handler) signal-alist)))))))
(set! $signal-interrupt-handler
(lambda (sig)
(let ((a (assq sig signal-alist)))
(unless a
($oops '$signal-interrupt-handler
"unexpected signal number ~d received~%"
sig))
((cdr a) sig)))))
;;; entry point from C kernel
(set! $scheme-init
(lambda ()
(set! debug-condition* '())
(collect-init)
($io-init)
(set! $session-key #f)
($interrupt reset)
($clear-pass-stats)
(enable-interrupts)))
(set! $scheme
(lambda (fns)
(define (go)
(call/cc
(lambda (k)
(parameterize ([abort-handler
(case-lambda [() (k -1)] [(x) (k x)])]
[exit-handler
(case-lambda [() (k (void))] [(x . args) (k x)])]
[reset-handler (lambda () (k -1))])
(apply (scheme-start) fns)))))
(unless (suppress-greeting)
(display ($scheme-greeting) (console-output-port))
(newline (console-output-port))
(flush-output-port (console-output-port)))
(if-feature expeditor
(if ($enable-expeditor) ($expeditor go) (go))
(go))))
(set! $script
(lambda (program? fn fns)
(define (go)
(call/cc
(lambda (k)
(parameterize ([abort-handler
(case-lambda [() (k -1)] [(x) (k x)])]
[exit-handler
(case-lambda [() (k (void))] [(x . args) (k x)])]
[reset-handler (lambda () (k -1))])
(apply (if program? (scheme-program) (scheme-script)) fn fns)))))
(if-feature expeditor
(if ($enable-expeditor) ($expeditor go) (go))
(go))))
(set! $as-time-goes-by
(lambda (e t)
(define sanitize
(lambda (s)
(define sanitize-time
(lambda (t)
(if (< (time-second t) 0)
(make-time 'time-duration 0 0)
t)))
(define sanitize-count
(lambda (n)
(max n 0)))
(make-sstats
(sanitize-time (sstats-cpu s))
(sanitize-time (sstats-real s))
(sanitize-count (sstats-bytes s))
(sanitize-count (sstats-gc-count s))
(sanitize-time (sstats-gc-cpu s))
(sanitize-time (sstats-gc-real s))
(sanitize-count (sstats-gc-bytes s)))))
(define prstats
(lambda (b1 b2)
(let ([a (statistics)])
(parameterize ([print-level 2] [print-length 2])
(fprintf (console-output-port) "(time ~s)~%" e))
(let ([elapsed (sstats-difference a b2)])
(let ([overhead (sstats-difference b2 b1)])
(let ([adjusted (sanitize (sstats-difference elapsed overhead))])
(sstats-print adjusted (console-output-port)))))
(flush-output-port (console-output-port)))))
(let ([b1 (statistics)])
(let ([b2 (statistics)])
(call-with-values t
(case-lambda
[(v) (prstats b1 b2) v]
[(v1 v2) (prstats b1 b2) (values v1 v2)]
[(v1 v2 v3) (prstats b1 b2) (values v1 v2 v3)]
[(v1 v2 v3 v4) (prstats b1 b2) (values v1 v2 v3 v4)]
[r (prstats b1 b2) (apply values r)]))))))
(set! $report-string
(lambda (dest what who msg args)
(let ([what (and (not (equal? what "")) what)]
[who (and (not (equal? who "")) who)])
(parameterize ([print-level 3] [print-length 6])
(format dest "~@[~@(~a~)~]~:[~; in ~]~@[~a~]~:[~;: ~]~@[~?~]"
what
(and what who)
who
(and (or what who) (not (equal? msg "")))
msg
args)))))
(let ()
(define report
(lambda (what who msg args)
(fresh-line (console-output-port))
($report-string (console-output-port) what who msg args)
(newline (console-output-port))
(flush-output-port (console-output-port))))
(set! break-handler
($make-thread-parameter
(case-lambda
[(who msg . args)
(unless (string? msg)
($oops 'default-break-handler "~s is not a string" msg))
(report "break" who msg args)
(($interrupt))]
[(who)
(report "break" who "" '())
(($interrupt))]
[()
(($interrupt))])
(lambda (x)
(unless (procedure? x)
($oops 'break-handler "~s is not a procedure" x))
x)))
)
(set-who! debug-condition
(case-lambda
[() (cond
[(assv ($tc-field 'threadno ($tc)) debug-condition*) => cdr]
[else #f])]
[(c)
(let ([n ($tc-field 'threadno ($tc))])
(with-tc-mutex
(set! debug-condition*
(let ([ls (remp (lambda (a) (eqv? (car a) n)) debug-condition*)])
(if c (cons (cons n c) ls) ls)))))]))
(set! debug
(lambda ()
(define line-limit 74)
(define pad
(lambda (s n p)
(let ([i (string-length s)])
(when (> n i) (display (make-string (- n i) #\space) p))
(display s p)
(max i n))))
(define numbered-line-display
(lambda (point? n c p)
(display (if point? "*" " "))
(let ([s (with-output-to-string (lambda () (display-condition c)))])
(let ([k (- line-limit (+ (pad (number->string n) 4 p) 2))])
(display ": " p)
(let ([i (string-length s)])
(if (> i k)
(fprintf p "~a ...~%" (substring s 0 (- k 4)))
(fprintf p "~a~%" s)))))))
(define unnumbered-line-display
(lambda (c p)
(let ([s (with-output-to-string (lambda () (display-condition c)))])
(let ([k (- line-limit 2)])
(display " " p)
(let ([i (string-length s)])
(if (> i k)
(fprintf p "~a ...~%" (substring s 0 (- k 4)))
(fprintf p "~a~%" s)))))))
(define printem
(lambda (point ls p)
(if (null? (cdr ls))
(let ([x (car ls)])
(unnumbered-line-display (cdr x) p))
(for-each
(lambda (x)
(numbered-line-display (eq? x point) (car x) (cdr x) p))
ls))))
(define debug-cafe
(lambda (point ls)
(parameterize ([$interrupt void])
(clear-input-port (console-input-port))
(let ([waiter (call/cc
(lambda (k)
(rec f (lambda () (k f)))))])
(fprintf (console-output-port) "debug> ")
(flush-output-port (console-output-port))
(let ([x (let ([x (parameterize ([$interrupt waiter]
[reset-handler waiter])
(read (console-input-port)))])
(if (eof-object? x)
(begin
(newline (console-output-port))
(flush-output-port (console-output-port))
'e)
x))])
(case x
[(i)
(let ([c (cdr point)])
(if (continuation-condition? c)
(inspect (condition-continuation c))
(display "the raise continuation is not available\n")))
(waiter)]
[(c)
(inspect (cdr point))
(waiter)]
[(q)
(with-tc-mutex
(for-each
(lambda (x) (set! debug-condition* (remq x debug-condition*)))
ls))
(void)]
[(e)
(void)]
[(s)
(printem point
(sort (lambda (x y) (< (car x) (car y))) ls)
(console-output-port))
(waiter)]
[(?)
(if (null? (cdr ls))
(fprintf (console-output-port)
"Type i to inspect the raise continuation (if available)
s to display the condition
c to inspect the condition
e or eof to exit the debugger, retaining error continuation
q to exit the debugger, discarding error continuation
")
(fprintf (console-output-port)
"Type i to inspect the selected thread's raise continuation (if available)
<n> to select thread <n>
s to display the conditions
c to inspect the selected thread's condition
e or eof to exit the debugger, retaining error continuations
q to exit the debugger, discarding error continuations
"))
(flush-output-port (console-output-port))
(waiter)]
[else
(cond
[(assv x ls) =>
(lambda (a)
(set! point a)
(waiter))]
[(and (integer? x) (nonnegative? x))
(fprintf (console-output-port)
"No saved error continuation for thread ~s.~%"
x)
(flush-output-port (console-output-port))
(waiter)]
[else
(fprintf (console-output-port)
"Invalid command. Type ? for options.~%")
(flush-output-port (console-output-port))
(waiter)])]))))))
(let ([ls debug-condition*])
(cond
[(null? ls)
(fprintf (console-output-port) "Nothing to debug.~%")
(flush-output-port (console-output-port))]
[else
(debug-cafe (car ls) ls)]))))
)
(define $collect-rendezvous
(lambda ()
(define once
(let ([once #f])
(lambda ()
(when (eq? once #t)
($oops '$collect-rendezvous
"cannot return to the collect-request-handler"))
(set! once #t))))
(if-feature pthreads
(with-tc-mutex
(let f ()
(when $collect-request-pending
(if (= $active-threads 1) ; last one standing
(dynamic-wind
once
(collect-request-handler)
(lambda ()
(set! $collect-request-pending #f)
(condition-broadcast $collect-cond)))
(begin
(condition-wait $collect-cond $tc-mutex)
(f))))))
(critical-section
(dynamic-wind
once
(collect-request-handler)
(lambda () (set! $collect-request-pending #f)))))))
(define collect-request-handler
(make-parameter
(lambda () (collect))
(lambda (x)
(unless (procedure? x)
($oops 'collect-request-handler "~s is not a procedure" x))
x)))
(define collect-notify (make-parameter #f (lambda (x) (and x #t))))
(define $c-error
(lambda (arg . error-args)
; error-args may be present along doargerr path, but we presently
; ignore them
(define-syntax c-error-case
(lambda (x)
(syntax-case x ()
[(_ arg [(key) fmls e1 e2 ...] ...)
(with-syntax ([(k ...) (map lookup-constant (datum (key ...)))])
#'(let ([t arg])
(record-case t
[(k) fmls e1 e2 ...]
...
[else ($oops '$c-error "invalid error type ~s" t)])))])))
(c-error-case arg
[(ERROR_OTHER) args (apply $oops args)]
[(ERROR_CALL_UNBOUND) (cnt symbol arg1?)
($oops #f "variable ~:s is not bound" symbol)]
[(ERROR_CALL_NONPROCEDURE_SYMBOL) (cnt symbol arg1?)
($oops #f "attempt to apply non-procedure ~s"
($top-level-value symbol))]
[(ERROR_CALL_NONPROCEDURE) (cnt nonprocedure arg1?)
($oops #f "attempt to apply non-procedure ~s" nonprocedure)]
[(ERROR_CALL_ARGUMENT_COUNT) (cnt procedure arg1?)
($oops #f "incorrect number of arguments to ~s" procedure)]
[(ERROR_RESET) (who msg . args)
($oops who "~?. Some debugging context lost" msg args)]
[(ERROR_NONCONTINUABLE_INTERRUPT) args
(let ([noncontinuable-interrupt
(lambda ()
((keyboard-interrupt-handler))
(fprintf (console-output-port)
"Noncontinuable interrupt.~%")
(reset))])
;; ruse to get inspector to print "continuation in
;; noncontinuable-interrupt" instead of "#c-error".
(noncontinuable-interrupt))]
[(ERROR_VALUES) (cnt)
($oops #f
"returned ~r values to single value return context"
cnt)]
[(ERROR_MVLET) (cnt)
($oops #f
"incorrect number of values received in multiple value context")])))
(define break
(lambda args
(apply (break-handler) args)))
(define timer-interrupt-handler
($make-thread-parameter
(lambda ()
($oops 'timer-interrupt
"timer interrupt occurred with no handler defined"))
(lambda (x)
(unless (procedure? x)
($oops 'timer-interrupt-handler "~s is not a procedure" x))
x)))
(define $symbol-type
(lambda (name)
(let ((flags ($sgetprop name '*flags* 0)))
(cond
[(any-set? (prim-mask system) flags) 'system]
[(any-set? (prim-mask primitive) flags) 'primitive]
[(any-set? (prim-mask keyword) flags)
(if (any-set? (prim-mask library-uid) flags)
'library-uid
'keyword)]
[(any-set? (prim-mask system-keyword) flags)
(if (any-set? (prim-mask library-uid) flags)
'system-library-uid
'system-keyword)]
[else 'unknown]))))
(let ()
; naive version is good enough for apropos
(define (substring? s1 s2)
(let ([n1 (string-length s1)] [n2 (string-length s2)])
(let loop2 ([i2 0])
(let loop1 ([i1 0] [j i2])
(if (fx= i1 n1)
i2
(and (not (fx= j n2))
(if (char=? (string-ref s1 i1) (string-ref s2 j))
(loop1 (fx+ i1 1) (fx+ j 1))
(loop2 (fx+ i2 1)))))))))
(define sym<? (lambda (x y) (string-ci<? (symbol->string x) (symbol->string y))))
(define apropos-help
(lambda (s env)
(let ([s (if (symbol? s) (symbol->string s) s)])
(sort sym<?
(let f ([ls (environment-symbols env)])
(if (null? ls)
'()
(if (substring? s (symbol->string (car ls)))
(cons (car ls) (f (cdr ls)))
(f (cdr ls)))))))))
(define apropos-library-help
(lambda (s)
(define lib<?
(lambda (lib1 lib2)
(and (not (null? lib2))
(or (null? lib1)
(if (eq? (car lib1) (car lib2))
(lib<? (cdr lib1) (cdr lib2))
(sym<? (car lib1) (car lib2)))))))
(let ([s (if (symbol? s) (symbol->string s) s)])
(sort (lambda (ls1 ls2) (lib<? (car ls1) (car ls2)))
(let do-libs ([lib* (library-list)] [match** '()])
(if (null? lib*)
match**
(do-libs (cdr lib*)
(let do-exports ([x* (library-exports (car lib*))] [match* '()])
(if (null? x*)
(if (null? match*)
match**
(cons (cons (car lib*) (sort sym<? match*)) match**))
(do-exports (cdr x*)
(if (substring? s (symbol->string (car x*)))
(cons (car x*) match*)
match*)))))))))))
(define check-s
(lambda (who s)
(unless (or (symbol? s) (string? s))
($oops who "~s is not a symbol or string" s))))
(define check-env
(lambda (who env)
(unless (environment? env)
($oops 'apropos-list "~s is not an environment" env))))
(set! apropos-list
(case-lambda
[(s)
(check-s 'apropos-list s)
(append
(apropos-help s (interaction-environment))
(apropos-library-help s))]
[(s env)
(check-s 'apropos-list s)
(check-env 'apropos-list env)
(append
(apropos-help s env)
(apropos-library-help s))]))
(let ()
(define do-apropos
(lambda (who where s env)
(printf "~a environment:\n ~{~<~%~& ~1:; ~s~>~^,~}~&" where (apropos-help s env))
(for-each
(lambda (x) (printf "~s:\n ~{~<~%~& ~1:; ~s~>~^,~}~&" (car x) (cdr x)))
(apropos-library-help s))))
(set-who! apropos
(case-lambda
[(s)
(check-s who s)
(do-apropos who "interaction" s (interaction-environment))]
[(s env)
(check-s who s)
(check-env who env)
(do-apropos who "supplied" s env)]))))
(let ()
(define-record-type pass-stats
(nongenerative)
(sealed #t)
(fields
(mutable calls)
(mutable cpu)
(mutable gc-cpu)
(mutable bytes))
(protocol
(lambda (n)
(lambda ()
(let ([t (make-time 'time-duration 0 0)])
(n 0 t t 0))))))
(define field-names '(name calls cpu gc-cpu bytes))
(define stats-ht)
(define-threaded outer-ps #f)
(set! $clear-pass-stats
(lambda ()
(set! stats-ht (make-eq-hashtable))))
(set! $enable-pass-timing (make-parameter #f))
(set-who! $pass-time
(lambda (name thunk)
(unless (symbol? name) ($oops who "~s is not a symbol" name))
(unless (procedure? thunk) ($oops who "~s is not a procedure" thunk))
(if ($enable-pass-timing)
(let ([ps (with-tc-mutex
(let ([a (hashtable-cell stats-ht name #f)])
(let ([ps (or (cdr a) (let ([ps (make-pass-stats)]) (set-cdr! a ps) ps))])
(pass-stats-calls-set! ps (+ (pass-stats-calls ps) 1))
ps)))])
(dynamic-wind
(lambda ()
(with-tc-mutex
(let ([cpu (current-time 'time-thread)]
[gc-cpu (current-time 'time-collector-cpu)]
[bytes (+ (bytes-deallocated) (bytes-allocated))])
(set-time-type! cpu 'time-duration)
(set-time-type! gc-cpu 'time-duration)
(when outer-ps
(pass-stats-cpu-set! outer-ps (add-duration (pass-stats-cpu outer-ps) cpu))
(pass-stats-gc-cpu-set! outer-ps (add-duration (pass-stats-gc-cpu outer-ps) gc-cpu))
(pass-stats-bytes-set! outer-ps (+ (pass-stats-bytes outer-ps) bytes)))
(pass-stats-cpu-set! ps (subtract-duration (pass-stats-cpu ps) cpu))
(pass-stats-gc-cpu-set! ps (subtract-duration (pass-stats-gc-cpu ps) gc-cpu))
(pass-stats-bytes-set! ps (- (pass-stats-bytes ps) bytes)))))
(lambda () (fluid-let ([outer-ps ps]) (thunk)))
(lambda ()
(with-tc-mutex
(let ([cpu (current-time 'time-thread)]
[gc-cpu (current-time 'time-collector-cpu)]
[bytes (+ (bytes-deallocated) (bytes-allocated))])
(set-time-type! cpu 'time-duration)
(set-time-type! gc-cpu 'time-duration)
(when outer-ps
(pass-stats-cpu-set! outer-ps (subtract-duration (pass-stats-cpu outer-ps) cpu))
(pass-stats-gc-cpu-set! outer-ps (subtract-duration (pass-stats-gc-cpu outer-ps) gc-cpu))
(pass-stats-bytes-set! outer-ps (- (pass-stats-bytes outer-ps) bytes)))
(pass-stats-cpu-set! ps (add-duration (pass-stats-cpu ps) cpu))
(pass-stats-gc-cpu-set! ps (add-duration (pass-stats-gc-cpu ps) gc-cpu))
(pass-stats-bytes-set! ps (+ (pass-stats-bytes ps) bytes)))))))
(thunk))))
(set-who! $pass-stats-fields (lambda () field-names))
(set! $pass-stats
(lambda ()
(define (build-result namev psv)
(vector->list
(vector-map
(lambda (name ps)
(list name
(pass-stats-calls ps)
(pass-stats-cpu ps)
(pass-stats-gc-cpu ps)
(pass-stats-bytes ps)))
namev
psv)))
(with-tc-mutex
(if outer-ps
(let ([cpu (current-time 'time-thread)]
[gc-cpu (current-time 'time-collector-cpu)]
[bytes (+ (bytes-deallocated) (bytes-allocated))])
(set-time-type! cpu 'time-duration)
(set-time-type! gc-cpu 'time-duration)
(pass-stats-cpu-set! outer-ps (add-duration (pass-stats-cpu outer-ps) cpu))
(pass-stats-gc-cpu-set! outer-ps (add-duration (pass-stats-gc-cpu outer-ps) gc-cpu))
(pass-stats-bytes-set! outer-ps (+ (pass-stats-bytes outer-ps) bytes))
(let ([result (call-with-values (lambda () (hashtable-entries stats-ht)) build-result)])
(pass-stats-cpu-set! outer-ps (subtract-duration (pass-stats-cpu outer-ps) cpu))
(pass-stats-gc-cpu-set! outer-ps (subtract-duration (pass-stats-gc-cpu outer-ps) gc-cpu))
(pass-stats-bytes-set! outer-ps (- (pass-stats-bytes outer-ps) bytes))
result))
(call-with-values (lambda () (hashtable-entries stats-ht)) build-result)))))
(let ()
(define who '$print-pass-stats)
(define field-name-strings (map symbol->string field-names))
(define check-psls
(lambda (psl*)
(unless (list? psl*) ($oops who "~s is not a list" psl*))
(for-each
(lambda (psl)
(define exact-integer? (lambda (x) (or (fixnum? x) (bignum? x))))
(unless (and (fx= ($list-length psl who) 5)
(apply (lambda (name calls cpu gc-cpu bytes)
(and (exact-integer? calls)
(time? cpu)
(time? gc-cpu)
(exact-integer? bytes)))
psl))
($oops who "malformed pass-stats entry ~s" psl)))
psl*)))
(define val->string
(lambda (x)
(cond
[(time? x)
(let-values ([(sec nsec)
(let ([sec (time-second x)] [nsec (time-nanosecond x)])
(if (and (< sec 0) (> nsec 0))
(values (+ sec 1) (- 1000000000 nsec))
(values sec nsec)))])
(format "~d.~9,'0d" sec nsec))]
[else (format "~s" x)])))
(define (print-pass-stats key psl*)
(define psl<?
(lambda (x y)
(apply (lambda (x-name x-calls x-cpu x-gc-cpu x-bytes)
(apply (lambda (y-name y-calls y-cpu y-gc-cpu y-bytes)
(case (or key 'non-gc-cpu)
[(non-gc-cpu)
(time<?
(time-difference x-cpu x-gc-cpu)
(time-difference y-cpu y-gc-cpu))]
[(cpu) (time<? x-cpu y-cpu)]
[(gc-cpu) (time<? x-gc-cpu y-gc-cpu)]
[(bytes) (< x-bytes y-bytes)]
[(name) (string<? (symbol->string x-name) (symbol->string y-name))]
[(calls) (< x-calls y-calls)]
[else ($oops who "unrecognized sort key ~s" key)]))
y))
x)))
; run check when passed psl* to check psl*; run when passed
; the value of ($pass-stats) to check our assumptions
(check-psls psl*)
(let ([psl* (append (sort psl<? psl*)
(list (let loop ([psl* psl*] [calls 0] [cpu (make-time 'time-duration 0 0)] [gc-cpu (make-time 'time-duration 0 0)] [bytes 0])
(if (null? psl*)
(list 'TOTAL calls cpu gc-cpu bytes)
(apply (lambda (*name *calls *cpu *gc-cpu *bytes)
(loop (cdr psl*)
(+ calls *calls)
(add-duration cpu *cpu)
(add-duration gc-cpu *gc-cpu)
(+ bytes *bytes)))
(car psl*))))))])
(let ([s** (map (lambda (psl) (map val->string psl)) psl*)])
(let ([w* (fold-left (lambda (w* s*)
(map (lambda (s w) (fxmax (string-length s) w)) s* w*))
(map string-length field-name-strings)
s**)])
(define print-row
(lambda (s*)
(printf "~v<~a~;~> " (car w*) (car s*))
(for-each (lambda (s w) (printf "~v:<~a~> " w s)) (cdr s*) (cdr w*))
(newline)))
(print-row field-name-strings)
(print-row (map (lambda (w) (make-string w #\-)) w*))
(for-each print-row s**)))))
(set! $print-pass-stats
(case-lambda
[() (print-pass-stats #f ($pass-stats))]
[(key) (print-pass-stats key ($pass-stats))]
[(key psl*) (print-pass-stats key psl*)]))))
)