per-expression limits
svn: r5828
This commit is contained in:
parent
da6e732918
commit
3d0a581f88
|
@ -644,12 +644,6 @@ by this function.
|
||||||
(which means that the contents may be unreliable, but the position
|
(which means that the contents may be unreliable, but the position
|
||||||
is). The default is `#f'.
|
is). The default is `#f'.
|
||||||
|
|
||||||
> (get-uncovered-expressions evaluator)
|
|
||||||
When this is used with an evaluator that was created with
|
|
||||||
`make-evaluator', it will return a list of uncovered syntax
|
|
||||||
objects. (It can also be provided as an argument to the evaluator,
|
|
||||||
with the same result.)
|
|
||||||
|
|
||||||
> namespace-specs
|
> namespace-specs
|
||||||
A parameter that holds a list of values that specify how to create a
|
A parameter that holds a list of values that specify how to create a
|
||||||
namespace for evaluation in `make-evaluator'. The first item in the
|
namespace for evaluation in `make-evaluator'. The first item in the
|
||||||
|
@ -724,18 +718,17 @@ by this function.
|
||||||
`get-output' returns the input end of the pipe.
|
`get-output' returns the input end of the pipe.
|
||||||
(Note that error output is *not* redirected.)
|
(Note that error output is *not* redirected.)
|
||||||
|
|
||||||
> (get-output evaluator)
|
> sandbox-eval-limits
|
||||||
When this is used with an evaluator that was created with
|
A parameter that determines the default limits on each use of a
|
||||||
`make-evaluator', it will return the output of the evaluator. (It
|
`make-evaluator' function. Its value should be a list of two
|
||||||
can also be provided as an argument to the evaluator, with the same
|
numbers, the first is a timeout value in seconds, and the second is
|
||||||
result.) The result depends on the value of the `sandbox-output'
|
for setting a memory limit in megabytes. Either one can be `#f' for
|
||||||
parameter at the time the evaluator was created: if it was `#f' then
|
disabling the corresponding limit (or the parameter can be set to
|
||||||
`get-output' will return `#f', if it was the symbol `pipe' then
|
`#f' to disble both). When limits are set `with-limits' (see blow)
|
||||||
`get-output' returns an input port that is being fed by the pipe,
|
is wrapped around any use of an evaluator, so consuming too much
|
||||||
and if it was the symbol `bytes' or `string' then `get-output'
|
time or memory results in an exception. After an evaluator is
|
||||||
returns the accumulated output and resets the evaluator's output to
|
generated, its limits can be modified using `set-eval-limits' below
|
||||||
a new output string or byte string (so each call returns a piece of
|
(the parameter value is used to initialize a new evaluator).
|
||||||
the evaluator's output).
|
|
||||||
|
|
||||||
> (make-evaluator language teachpack-paths input-program)
|
> (make-evaluator language teachpack-paths input-program)
|
||||||
This is the main entry point for the sandbox module.
|
This is the main entry point for the sandbox module.
|
||||||
|
@ -774,15 +767,46 @@ by this function.
|
||||||
See also `with-limits' below for adding resource limits, and
|
See also `with-limits' below for adding resource limits, and
|
||||||
`get-uncovered-expressions' above for enforcing test coverage.
|
`get-uncovered-expressions' above for enforcing test coverage.
|
||||||
|
|
||||||
|
> (get-output evaluator)
|
||||||
|
When this is used with an evaluator that was created with
|
||||||
|
`make-evaluator', it will return the output of the evaluator. (It
|
||||||
|
can also be provided as an argument to the evaluator, with the same
|
||||||
|
result.) The result depends on the value of the `sandbox-output'
|
||||||
|
parameter at the time the evaluator was created: if it was `#f' then
|
||||||
|
`get-output' will return `#f', if it was the symbol `pipe' then
|
||||||
|
`get-output' returns an input port that is being fed by the pipe,
|
||||||
|
and if it was the symbol `bytes' or `string' then `get-output'
|
||||||
|
returns the accumulated output and resets the evaluator's output to
|
||||||
|
a new output string or byte string (so each call returns a piece of
|
||||||
|
the evaluator's output).
|
||||||
|
|
||||||
|
> (get-uncovered-expressions evaluator)
|
||||||
|
When this is used with an evaluator that was created with
|
||||||
|
`make-evaluator', it will return a list of uncovered syntax
|
||||||
|
objects. (It can also be provided as an argument to the evaluator,
|
||||||
|
with the same result.)
|
||||||
|
|
||||||
|
> (set-eval-limits evaluator sec mb)
|
||||||
|
Changes the per-expression limits that the evaluator uses. This
|
||||||
|
procedure should be used to modify an evaluator limits -- changing
|
||||||
|
the `sandbox-eval-limits' parameter (see above) does not affect
|
||||||
|
existing evaluators. See also `with-limits' below.
|
||||||
|
|
||||||
> (call-with-limits sec mb thunk)
|
> (call-with-limits sec mb thunk)
|
||||||
This function executes the given thunk with memory and time
|
This function executes the given thunk with memory and time
|
||||||
restrictions: if execution consumes more than `mb' megabytes or more
|
restrictions: if execution consumes more than `mb' megabytes or more
|
||||||
that `sec' seconds, then the computation is aborted and an error is
|
that `sec' seconds, then the computation is aborted and an error is
|
||||||
thrown. Otherwise the result of the thunk is returned (a value,
|
thrown. Otherwise the result of the thunk is returned (a value,
|
||||||
multiple values, or raise an exception). Each of the two limits can
|
multiple values, or raise an exception). Each of the two limits can
|
||||||
be `#f' to disable it.
|
be `#f' to disable it. (Note: memory limits requires running in a
|
||||||
|
3m executable.)
|
||||||
|
|
||||||
(Note: memory limit requires running in a 3m executable.)
|
This is used in `make-evaluator' functions, according to the
|
||||||
|
`sandbox-eval-limits' setting and uses of `set-eval-limits': each
|
||||||
|
expression evaluation is protected from timeouts and memory
|
||||||
|
problems. This means that you normally would not use it -- but you
|
||||||
|
may want to limit a whole testing session instead of each expression
|
||||||
|
(eg, when you want to run tests faster).
|
||||||
|
|
||||||
> (with-limits sec mb body ...)
|
> (with-limits sec mb body ...)
|
||||||
A macro version of the above.
|
A macro version of the above.
|
||||||
|
|
|
@ -11,8 +11,10 @@
|
||||||
sandbox-path-permissions
|
sandbox-path-permissions
|
||||||
sandbox-input
|
sandbox-input
|
||||||
sandbox-output
|
sandbox-output
|
||||||
|
sandbox-eval-limits
|
||||||
get-output
|
get-output
|
||||||
get-uncovered-expressions
|
get-uncovered-expressions
|
||||||
|
set-eval-limits
|
||||||
make-evaluator
|
make-evaluator
|
||||||
call-with-limits
|
call-with-limits
|
||||||
with-limits)
|
with-limits)
|
||||||
|
@ -30,7 +32,7 @@
|
||||||
|
|
||||||
(define sandbox-input (make-parameter #f))
|
(define sandbox-input (make-parameter #f))
|
||||||
(define sandbox-output (make-parameter #f))
|
(define sandbox-output (make-parameter #f))
|
||||||
(define null-input (open-input-bytes #""))
|
(define sandbox-eval-limits (make-parameter '(30 10))) ; 30sec, 10mb
|
||||||
|
|
||||||
(define coverage-enabled (make-parameter #f))
|
(define coverage-enabled (make-parameter #f))
|
||||||
|
|
||||||
|
@ -264,99 +266,104 @@
|
||||||
(define current-eventspace (mz/mr (make-parameter #f) current-eventspace))
|
(define current-eventspace (mz/mr (make-parameter #f) current-eventspace))
|
||||||
(define make-eventspace (mz/mr void make-eventspace))
|
(define make-eventspace (mz/mr void make-eventspace))
|
||||||
(define run-in-bg (mz/mr thread queue-callback))
|
(define run-in-bg (mz/mr thread queue-callback))
|
||||||
|
(define null-input (open-input-bytes #""))
|
||||||
|
|
||||||
(define (get-uncovered-expressions eval) (eval get-uncovered-expressions))
|
(define (get-uncovered-expressions eval) (eval get-uncovered-expressions))
|
||||||
(define (get-output eval) (eval get-output))
|
(define (get-output eval) (eval get-output))
|
||||||
|
(define (set-eval-limits eval . args) (apply (eval set-eval-limits) args))
|
||||||
|
|
||||||
(define (make-evaluator language teachpacks input-program)
|
(define (make-evaluator language teachpacks input-program)
|
||||||
(let ([coverage-enabled (coverage-enabled)]
|
(define coverage? (coverage-enabled))
|
||||||
[uncovered-expressions #f]
|
(define uncovered-expressions #f)
|
||||||
[input-ch (make-channel)]
|
(define input-ch (make-channel))
|
||||||
[result-ch (make-channel)]
|
(define result-ch (make-channel))
|
||||||
[output #f])
|
(define output #f)
|
||||||
(parameterize
|
(define limits (sandbox-eval-limits))
|
||||||
([current-namespace (make-evaluation-namespace)]
|
(define (user-process)
|
||||||
[current-inspector (make-inspector)]
|
;; First read program and evaluate it as a module:
|
||||||
[current-library-collection-paths
|
(with-handlers ([void (lambda (exn) (channel-put result-ch exn))])
|
||||||
(filter directory-exists?
|
(evaluate-program
|
||||||
(append (sandbox-override-collection-paths)
|
language teachpacks input-program
|
||||||
(current-library-collection-paths)))]
|
(and coverage? (lambda (exprs) (set! uncovered-expressions exprs))))
|
||||||
[exit-handler (lambda x (error 'exit "user code cannot exit"))]
|
(channel-put result-ch 'ok))
|
||||||
[current-input-port
|
;; Now wait for interaction expressions:
|
||||||
(let ([inp (sandbox-input)]) (if inp (input->port inp) null-input))]
|
(let loop ()
|
||||||
[current-output-port
|
(let ([expr (channel-get input-ch)])
|
||||||
(let ([out (sandbox-output)])
|
(unless (eof-object? expr)
|
||||||
(cond [(not out) (open-output-nowhere)]
|
(with-handlers ([void (lambda (exn)
|
||||||
[(output-port? out) (set! output out) out]
|
(channel-put result-ch (cons 'exn exn)))])
|
||||||
[(eq? out 'pipe)
|
(let* ([sec (and limits (car limits))]
|
||||||
(let-values ([(i o) (make-pipe)]) (set! output i) o)]
|
[mb (and limits (cadr limits))]
|
||||||
[(memq out '(bytes string))
|
[run (if (or sec mb)
|
||||||
(let-values
|
(lambda () (with-limits sec mb (eval expr)))
|
||||||
([(open get)
|
(lambda () (eval expr)))])
|
||||||
(if (eq? out 'bytes)
|
(channel-put result-ch
|
||||||
(values open-output-bytes get-output-bytes)
|
(cons 'vals (call-with-values run list)))))
|
||||||
(values open-output-string get-output-string))])
|
(loop))))
|
||||||
(let ([o (open)])
|
(let loop ()
|
||||||
(set! output (lambda ()
|
(channel-put result-ch '(exn . nothing-more-to-evaluate))
|
||||||
(let ([o1 o])
|
(loop)))
|
||||||
(set! o (open))
|
(define (user-eval expr)
|
||||||
(current-output-port o)
|
(channel-put input-ch expr)
|
||||||
(get-output-bytes o1))))
|
(let ([r (channel-get result-ch)])
|
||||||
o))]
|
(if (eq? (car r) 'exn) (raise (cdr r)) (apply values (cdr r)))))
|
||||||
[else (error 'make-evaluator "bad output: ~e" out)]))]
|
(define (evaluator expr)
|
||||||
[sandbox-path-permissions
|
(cond [(eq? expr get-output)
|
||||||
(append (sandbox-path-permissions)
|
(if (procedure? output) (user-eval `(,output)) output)]
|
||||||
(get-lib-permissions (sandbox-override-collection-paths))
|
[(eq? expr get-uncovered-expressions)
|
||||||
(require-perms language teachpacks))]
|
uncovered-expressions]
|
||||||
[current-security-guard (sandbox-security-guard)])
|
[(eq? expr set-eval-limits)
|
||||||
;; Note the above definition of `current-eventspace': in MzScheme, it
|
(lambda args (set! limits args))]
|
||||||
;; is a parameter that is not used at all. Also note that creating an
|
[else (user-eval expr)]))
|
||||||
;; eventspace starts a thread that will eventually run the callback
|
(parameterize
|
||||||
;; code (which evaluates the program in `run-in-bg') -- so this
|
([current-namespace (make-evaluation-namespace)]
|
||||||
;; parameterization must be nested in the above, or it will not use the
|
[current-inspector (make-inspector)]
|
||||||
;; new namespace.
|
[current-library-collection-paths
|
||||||
(parameterize ([current-eventspace (make-eventspace)])
|
(filter directory-exists?
|
||||||
(run-in-bg
|
(append (sandbox-override-collection-paths)
|
||||||
(lambda ()
|
(current-library-collection-paths)))]
|
||||||
;; First read program and evaluate it as a module:
|
[exit-handler (lambda x (error 'exit "user code cannot exit"))]
|
||||||
(with-handlers ([void (lambda (exn) (channel-put result-ch exn))])
|
[current-input-port
|
||||||
(evaluate-program
|
(let ([inp (sandbox-input)]) (if inp (input->port inp) null-input))]
|
||||||
language teachpacks input-program
|
[current-output-port
|
||||||
(and coverage-enabled
|
(let ([out (sandbox-output)])
|
||||||
(lambda (exprs) (set! uncovered-expressions exprs))))
|
(cond [(not out) (open-output-nowhere)]
|
||||||
(channel-put result-ch 'ok))
|
[(output-port? out) (set! output out) out]
|
||||||
;; Now wait for interaction expressions:
|
[(eq? out 'pipe)
|
||||||
(let loop ()
|
(let-values ([(i o) (make-pipe)]) (set! output i) o)]
|
||||||
(let ([expr (channel-get input-ch)])
|
[(memq out '(bytes string))
|
||||||
(unless (eof-object? expr)
|
(let-values
|
||||||
(with-handlers ([void (lambda (exn)
|
([(open get)
|
||||||
(channel-put result-ch
|
(if (eq? out 'bytes)
|
||||||
(cons 'exn exn)))])
|
(values open-output-bytes get-output-bytes)
|
||||||
(channel-put result-ch
|
(values open-output-string get-output-string))])
|
||||||
(cons 'vals (call-with-values
|
(let ([o (open)])
|
||||||
(lambda () (eval expr))
|
(set! output (lambda ()
|
||||||
list))))
|
(let ([o1 o])
|
||||||
(loop))))
|
(set! o (open))
|
||||||
(let loop ()
|
(current-output-port o)
|
||||||
(channel-put result-ch '(exn . no-more-to-evaluate))
|
(get-output-bytes o1))))
|
||||||
(loop))))
|
o))]
|
||||||
(let ([r (channel-get result-ch)])
|
[else (error 'make-evaluator "bad output: ~e" out)]))]
|
||||||
(define (eval-in-user-context expr)
|
[sandbox-path-permissions
|
||||||
(channel-put input-ch expr)
|
(append (sandbox-path-permissions)
|
||||||
(let ([r (channel-get result-ch)])
|
(get-lib-permissions (sandbox-override-collection-paths))
|
||||||
(if (eq? (car r) 'exn) (raise (cdr r)) (apply values (cdr r)))))
|
(require-perms language teachpacks))]
|
||||||
(if (eq? r 'ok)
|
[current-security-guard (sandbox-security-guard)])
|
||||||
;; Initial program executed ok, so return an evaluator:
|
;; Note the above definition of `current-eventspace': in MzScheme, it is
|
||||||
(lambda (expr)
|
;; a parameter that is not used at all. Also note that creating an
|
||||||
(cond [(eq? expr get-uncovered-expressions)
|
;; eventspace starts a thread that will eventually run the callback code
|
||||||
uncovered-expressions]
|
;; (which evaluates the program in `run-in-bg') -- so this
|
||||||
[(eq? expr get-output)
|
;; parameterization must be nested in the above, or it will not use the
|
||||||
(if (procedure? output)
|
;; new namespace.
|
||||||
(eval-in-user-context `(,output))
|
(parameterize ([current-eventspace (make-eventspace)])
|
||||||
output)]
|
(run-in-bg user-process)
|
||||||
[else (eval-in-user-context expr)]))
|
(let ([r (channel-get result-ch)])
|
||||||
;; Program didn't execute:
|
(if (eq? r 'ok)
|
||||||
(raise r)))))))
|
;; Initial program executed ok, so return an evaluator:
|
||||||
|
evaluator
|
||||||
|
;; Program didn't execute:
|
||||||
|
(raise r))))))
|
||||||
|
|
||||||
;; Resources ----------------------------------------------------------------
|
;; Resources ----------------------------------------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user