mirror of
https://github.com/NixOS/nix.git
synced 2025-11-08 19:46:02 +01:00
This fixes a segfault on infinite function call recursion (rather than
infinite thunk recursion) by tracking the function call depth in
`EvalState`.
Additionally, to avoid printing extremely long stack traces, stack
frames are now deduplicated, with a `(19997 duplicate traces omitted)`
message. This should only really be triggered in infinite recursion
scenarios.
Before:
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
Segmentation fault: 11
After:
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
error: stack overflow
at «string»:1:14:
1| (x: x x) (x: x x)
| ^
$ nix-instantiate --eval --expr '(x: x x) (x: x x)' --show-trace
error:
… from call site
at «string»:1:1:
1| (x: x x) (x: x x)
| ^
… while calling anonymous lambda
at «string»:1:2:
1| (x: x x) (x: x x)
| ^
… from call site
at «string»:1:5:
1| (x: x x) (x: x x)
| ^
… while calling anonymous lambda
at «string»:1:11:
1| (x: x x) (x: x x)
| ^
… from call site
at «string»:1:14:
1| (x: x x) (x: x x)
| ^
(19997 duplicate traces omitted)
error: stack overflow
at «string»:1:14:
1| (x: x x) (x: x x)
| ^
36 lines
1 KiB
Nix
36 lines
1 KiB
Nix
# Check that stack frame deduplication only affects consecutive intervals, and
|
|
# that they are reported independently of any preceding sections, even if
|
|
# they're indistinguishable.
|
|
#
|
|
# In terms of the current implementation, we check that we clear the set of
|
|
# "seen frames" after eliding a group of frames.
|
|
#
|
|
# Suppose we have:
|
|
# - 10 frames in a function A
|
|
# - 10 frames in a function B
|
|
# - 10 frames in a function A
|
|
#
|
|
# We want to output:
|
|
# - a few frames of A (skip the rest)
|
|
# - a few frames of B (skip the rest)
|
|
# - a few frames of A (skip the rest)
|
|
#
|
|
# If we implemented this in the naive manner, we'd instead get:
|
|
# - a few frames of A (skip the rest)
|
|
# - a few frames of B (skip the rest, _and_ skip the remaining frames of A)
|
|
let
|
|
throwAfterB = recurse: n:
|
|
if n > 0
|
|
then throwAfterB recurse (n - 1)
|
|
else if recurse
|
|
then throwAfterA false 10
|
|
else throw "Uh oh!";
|
|
|
|
throwAfterA = recurse: n:
|
|
if n > 0
|
|
then throwAfterA recurse (n - 1)
|
|
else if recurse
|
|
then throwAfterB true 10
|
|
else throw "Uh oh!";
|
|
in
|
|
throwAfterA true 10
|