A test binary names the signal that killed a daemon, and the break buffer keeps its keys under Evil
This commit is contained in:
commit
f986c0728a
44
TODO.org
44
TODO.org
@ -1649,16 +1649,22 @@ sanitizer flag to it. Named as the check worth adding next; a day rather than an
|
|||||||
hour. The x86 backend is not a gap here — that pair is refused by name, because
|
hour. The x86 backend is not a gap here — that pair is refused by name, because
|
||||||
there is no sanitizer pass over hand-written assembly.
|
there is no sanitizer pass over hand-written assembly.
|
||||||
|
|
||||||
** TODO A transient signal 11 on a globals daemon
|
** DONE A transient signal 11 on a globals daemon
|
||||||
Seen once, never reproduced, on a daemon whose fixture had just gained a host
|
CLOSED: [2026-09-25]
|
||||||
global =Vec= and a run-time-new one. A reproduction under load would settle it.
|
Not a segfault. The report was OCaml's signal number, and in OCaml's numbering
|
||||||
|
-11 is SIGTERM (SIGSEGV is -10). Nothing in the daemon sends itself SIGTERM, so
|
||||||
|
it was killed from outside. The test binaries now print a signal by name
|
||||||
|
(=Test_support.signal_name=); a number from =WSIGNALED= is never printed raw.
|
||||||
|
|
||||||
** TODO test_dev daemons fail to bind under load
|
** DONE test_dev daemons fail to bind under load
|
||||||
Daemons exiting with status 1 or 2 before binding their socket, across most of the
|
CLOSED: [2026-09-25]
|
||||||
file at once, while the load average is high. No stale socket or leftover daemon
|
The failure was a full /tmp, not the load. /tmp is a tmpfs, and every session
|
||||||
afterwards; green on a quiet machine. Distinct from the registry race and the
|
left its =flan-dev-<pid>= build directory behind (a test_dev run leaves about
|
||||||
stale-park re-run flake, both of which are fixed. A daemon that dies before it
|
300MB); a few concurrent runs filled it and each daemon after that died on its
|
||||||
binds died on the compiler's side, before any program it built has run a line.
|
link with ENOSPC. The fix is the session removing its own directory on a clean
|
||||||
|
=close=, which the build-plumbing lane owns. The half-write test's abort also
|
||||||
|
goes through =aborted= now, which took the same run down with an uncaught
|
||||||
|
=Wire.Closed=.
|
||||||
|
|
||||||
** DONE A test binary that hangs is killed by its own alarm
|
** DONE A test binary that hangs is killed by its own alarm
|
||||||
A reader branch that forgets to advance loops for ever and the suite waits as long
|
A reader branch that forgets to advance loops for ever and the suite waits as long
|
||||||
@ -1799,13 +1805,21 @@ function of that name. Not reproduced: the file type checks, =flan reload= of
|
|||||||
the same form builds, and a minimal =defclass= + =get= + =return= program
|
the same form builds, and a minimal =defclass= + =get= + =return= program
|
||||||
compiles. So it is the session path against an installed program, and what is
|
compiles. So it is the session path against an installed program, and what is
|
||||||
missing is what that daemon had installed at the time.
|
missing is what that daemon had installed at the time.
|
||||||
|
Also not reproduced against a live daemon (2026-09-25): an =eval= of the
|
||||||
|
=defclass=, of a =defn= doing the =get= and =return=, and of both in one form,
|
||||||
|
and an =eval-expr= of the =get=, all succeed. In a session every installed
|
||||||
|
function of no arguments returning =()= has the type =(CFn [] ())=, not only
|
||||||
|
=pause= — a bare =pause= or =tick= asked of the session says so — so the
|
||||||
|
keyword may not be what resolved. The next report wants the exact form sent.
|
||||||
|
|
||||||
** TODO A digit does not take the restart RET takes
|
** DONE A digit does not take the restart RET takes
|
||||||
Pressing =0= left the program stopped; RET on the same line resumed it. Both
|
CLOSED: [2026-09-25]
|
||||||
end in =flan-cnr-take=, but the digit path (=flan-cnr-take-number=,
|
Evil's normal state binds =0= (beginning of line), =1=-=9= (a count) and RET
|
||||||
=flan-cnr.el:559=) scans from =point-min= for the line whose =flan-cnr-index=
|
above the major mode's map, so the digit never reached =flan-cnr-take-number=.
|
||||||
matches and calls =take= inside a =save-excursion=. Not reproduced yet — needs
|
The keys =flan-cnr-mode-map= itself binds are given to Evil's normal and
|
||||||
a non-raylib program stopped under a test daemon.
|
motion states in that mode; every other key, including what =special-mode-map=
|
||||||
|
binds, stays Evil's. The other special-mode buffers (inspect, watch, doc, disassembly,
|
||||||
|
diagnostics, lower) have the same exposure and are not changed.
|
||||||
|
|
||||||
** TODO Eval in the frame, from the break loop
|
** TODO Eval in the frame, from the break loop
|
||||||
An expression is evaluated at a frame boundary, so it sees globals and not the
|
An expression is evaluated at a frame boundary, so it sees globals and not the
|
||||||
|
|||||||
@ -702,6 +702,24 @@ anyone who would rather TAB always moved."
|
|||||||
map)
|
map)
|
||||||
"Keys in `flan-cnr-mode'.")
|
"Keys in `flan-cnr-mode'.")
|
||||||
|
|
||||||
|
;; Evil's normal state binds 0, the other digits and RET above any major
|
||||||
|
;; mode's map, so under Evil a digit moved point or started a count and took
|
||||||
|
;; nothing. The keys this file binds are given to Evil's normal and motion
|
||||||
|
;; states in this mode. Only those keys: `flan-cnr-mode-map' inherits
|
||||||
|
;; `special-mode-map', and making it an overriding map would carry h, SPC, <
|
||||||
|
;; and - over from there as well. Every key not listed stays Evil's.
|
||||||
|
(with-eval-after-load 'evil
|
||||||
|
(when (fboundp 'evil-define-key*)
|
||||||
|
;; Collected first, and without the parent's bindings, because
|
||||||
|
;; `evil-define-key*' writes into the map being walked.
|
||||||
|
(let ((own nil))
|
||||||
|
(map-keymap-internal (lambda (key def)
|
||||||
|
(when (commandp def) (push (cons key def) own)))
|
||||||
|
flan-cnr-mode-map)
|
||||||
|
(dolist (b own)
|
||||||
|
(evil-define-key* '(normal motion) flan-cnr-mode-map
|
||||||
|
(vector (car b)) (cdr b))))))
|
||||||
|
|
||||||
(define-derived-mode flan-cnr-mode special-mode "flan-break"
|
(define-derived-mode flan-cnr-mode special-mode "flan-break"
|
||||||
"What a stopped Flan program is offering."
|
"What a stopped Flan program is offering."
|
||||||
(setq buffer-read-only t))
|
(setq buffer-read-only t))
|
||||||
|
|||||||
@ -1704,6 +1704,55 @@ stopped program, which is the case where it should fire."
|
|||||||
(file-name-directory load-file-name))
|
(file-name-directory load-file-name))
|
||||||
nil t)
|
nil t)
|
||||||
|
|
||||||
|
;; The break buffer's keys under Evil, pressed through the command loop rather
|
||||||
|
;; than called. Evil's normal state binds 0 (beginning of line), 1-9 (a count)
|
||||||
|
;; and RET (next line) above any major mode's map, so without the buffer's map
|
||||||
|
;; taking precedence a digit moved point and took nothing. Run only where
|
||||||
|
;; Evil is installed: -Q loads no packages, so it is looked for in the two
|
||||||
|
;; places a package install puts it, and turned off again afterwards so nothing
|
||||||
|
;; above or below this runs under it.
|
||||||
|
(let* ((dirs (append (file-expand-wildcards "~/.config/emacs/elpa/evil-[0-9]*")
|
||||||
|
(file-expand-wildcards "~/.emacs.d/elpa/evil-[0-9]*")
|
||||||
|
(file-expand-wildcards "~/.config/emacs/elpa/goto-chg-*")
|
||||||
|
(file-expand-wildcards "~/.emacs.d/elpa/goto-chg-*")))
|
||||||
|
(load-path (append dirs load-path)))
|
||||||
|
(if (not (require 'evil nil t))
|
||||||
|
(message " skip the break buffer under Evil (Evil is not installed)")
|
||||||
|
(evil-mode 1)
|
||||||
|
(unwind-protect
|
||||||
|
(let* ((sent nil)
|
||||||
|
(flan-cnr-request-function
|
||||||
|
(lambda (form) (setq sent form) (list :status "ok")))
|
||||||
|
(buf (test-flan--cnr
|
||||||
|
(list :condition "Missing" :restarts '("retry" "skip")))))
|
||||||
|
(switch-to-buffer buf)
|
||||||
|
(evil-initialize-state)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(execute-kbd-macro (kbd "0"))
|
||||||
|
(test-flan--check "under Evil, 0 takes restart 0"
|
||||||
|
(equal sent '(:op "restart-at" :index 0 :name "retry")))
|
||||||
|
(setq sent nil)
|
||||||
|
(switch-to-buffer buf)
|
||||||
|
(execute-kbd-macro (kbd "1"))
|
||||||
|
(test-flan--check "under Evil, 1 takes restart 1"
|
||||||
|
(equal sent '(:op "restart-at" :index 1 :name "skip")))
|
||||||
|
(setq sent nil)
|
||||||
|
(switch-to-buffer buf)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(search-forward " 1: ")
|
||||||
|
(execute-kbd-macro (kbd "RET"))
|
||||||
|
(test-flan--check "under Evil, RET takes the restart on its line"
|
||||||
|
(equal sent '(:op "restart-at" :index 1 :name "skip")))
|
||||||
|
;; And the keys the buffer does not bind are still Evil's, not the
|
||||||
|
;; ones `special-mode-map' would bring with it.
|
||||||
|
(switch-to-buffer buf)
|
||||||
|
(dolist (k '(("h" . evil-backward-char) ("SPC" . evil-forward-char)
|
||||||
|
("<" . evil-shift-left)
|
||||||
|
("-" . evil-previous-line-first-non-blank)))
|
||||||
|
(test-flan--check (format "under Evil, %s is still Evil's" (car k))
|
||||||
|
(eq (key-binding (kbd (car k))) (cdr k)))))
|
||||||
|
(evil-mode -1))))
|
||||||
|
|
||||||
(message "\n%d checks, %d failures" test-flan--ran test-flan--failures)
|
(message "\n%d checks, %d failures" test-flan--ran test-flan--failures)
|
||||||
(kill-emacs (if (> test-flan--failures 0) 1 0))
|
(kill-emacs (if (> test-flan--failures 0) 1 0))
|
||||||
|
|
||||||
|
|||||||
@ -186,8 +186,8 @@ module Pool = struct
|
|||||||
name
|
name
|
||||||
(match status with
|
(match status with
|
||||||
| Unix.WEXITED n -> Printf.sprintf "with code %d" n
|
| Unix.WEXITED n -> Printf.sprintf "with code %d" n
|
||||||
| Unix.WSIGNALED n -> Printf.sprintf "on signal %d" n
|
| Unix.WSIGNALED n -> "on " ^ Test_support.signal_name n
|
||||||
| Unix.WSTOPPED n -> Printf.sprintf "stopped on signal %d" n))
|
| Unix.WSTOPPED n -> "stopped on " ^ Test_support.signal_name n))
|
||||||
| None, _ ->
|
| None, _ ->
|
||||||
Some
|
Some
|
||||||
(Printf.sprintf
|
(Printf.sprintf
|
||||||
@ -195,8 +195,8 @@ module Pool = struct
|
|||||||
name
|
name
|
||||||
(match status with
|
(match status with
|
||||||
| Unix.WEXITED n -> Printf.sprintf "exit %d" n
|
| Unix.WEXITED n -> Printf.sprintf "exit %d" n
|
||||||
| Unix.WSIGNALED n -> Printf.sprintf "signal %d" n
|
| Unix.WSIGNALED n -> Test_support.signal_name n
|
||||||
| Unix.WSTOPPED n -> Printf.sprintf "stopped, signal %d" n))
|
| Unix.WSTOPPED n -> "stopped on " ^ Test_support.signal_name n))
|
||||||
in
|
in
|
||||||
ignore (Queue.pop inflight);
|
ignore (Queue.pop inflight);
|
||||||
match msg with
|
match msg with
|
||||||
|
|||||||
@ -149,8 +149,8 @@ let () =
|
|||||||
fail "agent reload\n got: %S (%s)\n wanted: %S" text
|
fail "agent reload\n got: %S (%s)\n wanted: %S" text
|
||||||
(match status with
|
(match status with
|
||||||
| Unix.WEXITED c -> Printf.sprintf "exit %d" c
|
| Unix.WEXITED c -> Printf.sprintf "exit %d" c
|
||||||
| Unix.WSIGNALED c -> Printf.sprintf "signal %d" c
|
| Unix.WSIGNALED c -> Test_support.signal_name c
|
||||||
| Unix.WSTOPPED c -> Printf.sprintf "stopped %d" c)
|
| Unix.WSTOPPED c -> "stopped on " ^ Test_support.signal_name c)
|
||||||
"1\n1000\n1007\n"
|
"1\n1000\n1007\n"
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -309,8 +309,8 @@ let () =
|
|||||||
%S (exit 1)" text
|
%S (exit 1)" text
|
||||||
(match !bstat with
|
(match !bstat with
|
||||||
| Unix.WEXITED c -> Printf.sprintf "exit %d" c
|
| Unix.WEXITED c -> Printf.sprintf "exit %d" c
|
||||||
| Unix.WSIGNALED c -> Printf.sprintf "signal %d" c
|
| Unix.WSIGNALED c -> Test_support.signal_name c
|
||||||
| Unix.WSTOPPED c -> Printf.sprintf "stopped %d" c)
|
| Unix.WSTOPPED c -> "stopped on " ^ Test_support.signal_name c)
|
||||||
"cannot listen\n"
|
"cannot listen\n"
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -766,8 +766,8 @@ let () =
|
|||||||
fail "abort left status %s, wanted exit 134"
|
fail "abort left status %s, wanted exit 134"
|
||||||
(match !lstatus with
|
(match !lstatus with
|
||||||
| Unix.WEXITED c -> Printf.sprintf "exit %d" c
|
| Unix.WEXITED c -> Printf.sprintf "exit %d" c
|
||||||
| Unix.WSIGNALED c -> Printf.sprintf "signal %d" c
|
| Unix.WSIGNALED c -> Test_support.signal_name c
|
||||||
| Unix.WSTOPPED c -> Printf.sprintf "stopped %d" c)
|
| Unix.WSTOPPED c -> "stopped on " ^ Test_support.signal_name c)
|
||||||
(* The other way out, and the one that skips atexit on purpose: [abort]
|
(* The other way out, and the one that skips atexit on purpose: [abort]
|
||||||
leaves by [_exit] so that it cannot hang on the loader lock, and the
|
leaves by [_exit] so that it cannot hang on the loader lock, and the
|
||||||
socket is therefore unlinked by hand there. A file left here would
|
socket is therefore unlinked by hand there. A file left here would
|
||||||
|
|||||||
@ -196,6 +196,23 @@ let () =
|
|||||||
if not (contains_sub other "flan.abi.x86") then
|
if not (contains_sub other "flan.abi.x86") then
|
||||||
fail "a parked program's other refusals were rewritten too: %S" other
|
fail "a parked program's other refusals were rewritten too: %S" other
|
||||||
|
|
||||||
|
(* A daemon's death is reported by the signal's name. [WSIGNALED] carries
|
||||||
|
OCaml's own numbering, in which SIGTERM is -11, and a SIGTERM printed as
|
||||||
|
"signal -11" was once recorded as a segfault. A real process, really
|
||||||
|
terminated, so the number is whatever [waitpid] hands back. *)
|
||||||
|
let () =
|
||||||
|
let p =
|
||||||
|
Unix.create_process "sleep" [| "sleep"; "30" |] Unix.stdin Unix.stdout
|
||||||
|
Unix.stderr
|
||||||
|
in
|
||||||
|
Unix.kill p Sys.sigterm;
|
||||||
|
match Unix.waitpid [] p with
|
||||||
|
| _, Unix.WSIGNALED n when Test_support.signal_name n = "SIGTERM" -> ()
|
||||||
|
| _, Unix.WSIGNALED n ->
|
||||||
|
fail "a process ended by SIGTERM is reported as %s"
|
||||||
|
(Test_support.signal_name n)
|
||||||
|
| _, _ -> fail "a process sent SIGTERM did not end on a signal"
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
match Sys.command "command -v clang > /dev/null 2>&1 && command -v llc > /dev/null 2>&1" with
|
match Sys.command "command -v clang > /dev/null 2>&1 && command -v llc > /dev/null 2>&1" with
|
||||||
| 0 ->
|
| 0 ->
|
||||||
@ -5104,8 +5121,8 @@ let () =
|
|||||||
(match st with
|
(match st with
|
||||||
| Unix.WEXITED n -> Printf.sprintf "exit %d" n
|
| Unix.WEXITED n -> Printf.sprintf "exit %d" n
|
||||||
| Unix.WSIGNALED n when n = Sys.sigpipe -> "killed by SIGPIPE"
|
| Unix.WSIGNALED n when n = Sys.sigpipe -> "killed by SIGPIPE"
|
||||||
| Unix.WSIGNALED n -> Printf.sprintf "signal %d" n
|
| Unix.WSIGNALED n -> Test_support.signal_name n
|
||||||
| Unix.WSTOPPED n -> Printf.sprintf "stopped on %d" n);
|
| Unix.WSTOPPED n -> "stopped on " ^ Test_support.signal_name n);
|
||||||
None
|
None
|
||||||
| exception Unix.Unix_error _ -> Some (connect rsock)
|
| exception Unix.Unix_error _ -> Some (connect rsock)
|
||||||
in
|
in
|
||||||
@ -5605,9 +5622,12 @@ let () =
|
|||||||
abort is refused by a program that is *running*, which is what a
|
abort is refused by a program that is *running*, which is what a
|
||||||
failure above would leave behind — and this fixture then polls for
|
failure above would leave behind — and this fixture then polls for
|
||||||
twenty seconds and parks, so the wait below would be a hang rather
|
twenty seconds and parks, so the wait below would be a hang rather
|
||||||
than a report. The signal is for that case only. *)
|
than a report. The signal is for that case only. Through [aborted],
|
||||||
if status (request c "(:op \"abort\")") <> "ok" then
|
because an abort that worked can arrive as the socket closing. *)
|
||||||
(try Unix.kill hpid Sys.sigkill with Unix.Unix_error _ -> ());
|
(match aborted c with
|
||||||
|
| Some r when status r <> "ok" ->
|
||||||
|
(try Unix.kill hpid Sys.sigkill with Unix.Unix_error _ -> ())
|
||||||
|
| _ -> ());
|
||||||
(try Unix.close c with Unix.Unix_error _ -> ());
|
(try Unix.close c with Unix.Unix_error _ -> ());
|
||||||
(try ignore (Unix.waitpid [] hpid) with Unix.Unix_error _ -> ())
|
(try ignore (Unix.waitpid [] hpid) with Unix.Unix_error _ -> ())
|
||||||
end;
|
end;
|
||||||
|
|||||||
@ -112,9 +112,10 @@ let () =
|
|||||||
match !status with
|
match !status with
|
||||||
| Some (Unix.WEXITED n) -> Printf.sprintf "exited with status %d" n
|
| Some (Unix.WEXITED n) -> Printf.sprintf "exited with status %d" n
|
||||||
| Some (Unix.WSIGNALED n) ->
|
| Some (Unix.WSIGNALED n) ->
|
||||||
Printf.sprintf "was killed by signal %d — nothing ran its cleanup, so \
|
Printf.sprintf "was killed by %s — nothing ran its cleanup, so \
|
||||||
the socket file is stale rather than gone" n
|
the socket file is stale rather than gone"
|
||||||
| Some (Unix.WSTOPPED n) -> Printf.sprintf "stopped on signal %d" n
|
(Test_support.signal_name n)
|
||||||
|
| Some (Unix.WSTOPPED n) -> Printf.sprintf "stopped on %s" (Test_support.signal_name n)
|
||||||
| None -> "was still running when the client finished, and was terminated"
|
| None -> "was still running when the client finished, and was terminated"
|
||||||
in
|
in
|
||||||
(* Read before the removal below, because on a failure this is the evidence
|
(* Read before the removal below, because on a failure this is the evidence
|
||||||
|
|||||||
@ -326,9 +326,11 @@ let () =
|
|||||||
"the daemon had already been killed by SIGPIPE — a reply written \
|
"the daemon had already been killed by SIGPIPE — a reply written \
|
||||||
into a socket whose reader had gone"
|
into a socket whose reader had gone"
|
||||||
| Some (Unix.WSIGNALED n) ->
|
| Some (Unix.WSIGNALED n) ->
|
||||||
Printf.printf "the daemon had already been killed by signal %d\n" n
|
Printf.printf "the daemon had already been killed by %s\n"
|
||||||
|
(Test_support.signal_name n)
|
||||||
| Some (Unix.WSTOPPED n) ->
|
| Some (Unix.WSTOPPED n) ->
|
||||||
Printf.printf "the daemon was stopped on signal %d\n" n
|
Printf.printf "the daemon was stopped on %s\n"
|
||||||
|
(Test_support.signal_name n)
|
||||||
| None -> print_endline "the daemon was still running at the end");
|
| None -> print_endline "the daemon was still running at the end");
|
||||||
Printf.printf "\n-- the program's own output, last 4k --\n%s\n" prog_out;
|
Printf.printf "\n-- the program's own output, last 4k --\n%s\n" prog_out;
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@ -152,6 +152,24 @@ let rec connect ?(ms = 5000) path =
|
|||||||
thing after a failed wait is always the report of it. *)
|
thing after a failed wait is always the report of it. *)
|
||||||
let listen_why = ref ""
|
let listen_why = ref ""
|
||||||
|
|
||||||
|
(* A signal as [WSIGNALED] carries it, by name. OCaml numbers the signals it
|
||||||
|
knows negatively and in its own order — [Sys.sigterm] is -11 and
|
||||||
|
[Sys.sigsegv] is -10 — so printing the number reads as the wrong signal to
|
||||||
|
anyone who knows the POSIX table: a daemon terminated by SIGTERM was once
|
||||||
|
recorded here as "a transient signal 11", a segfault that never happened. *)
|
||||||
|
let signal_name n =
|
||||||
|
let known =
|
||||||
|
[ Sys.sigabrt, "SIGABRT"; Sys.sigalrm, "SIGALRM"; Sys.sigfpe, "SIGFPE";
|
||||||
|
Sys.sighup, "SIGHUP"; Sys.sigill, "SIGILL"; Sys.sigint, "SIGINT";
|
||||||
|
Sys.sigkill, "SIGKILL"; Sys.sigpipe, "SIGPIPE"; Sys.sigquit, "SIGQUIT";
|
||||||
|
Sys.sigsegv, "SIGSEGV"; Sys.sigterm, "SIGTERM"; Sys.sigusr1, "SIGUSR1";
|
||||||
|
Sys.sigusr2, "SIGUSR2"; Sys.sigchld, "SIGCHLD"; Sys.sigbus, "SIGBUS";
|
||||||
|
Sys.sigtrap, "SIGTRAP"; Sys.sigxcpu, "SIGXCPU" ]
|
||||||
|
in
|
||||||
|
match List.assoc_opt n known with
|
||||||
|
| Some s -> s
|
||||||
|
| None -> Printf.sprintf "signal %d" n
|
||||||
|
|
||||||
let listening ?(ms = 30000) ~pid path =
|
let listening ?(ms = 30000) ~pid path =
|
||||||
let died = ref None in
|
let died = ref None in
|
||||||
ignore
|
ignore
|
||||||
@ -171,10 +189,10 @@ let listening ?(ms = 30000) ~pid path =
|
|||||||
| Some (Unix.WEXITED n) ->
|
| Some (Unix.WEXITED n) ->
|
||||||
Printf.sprintf "exited with status %d before binding %s" n path
|
Printf.sprintf "exited with status %d before binding %s" n path
|
||||||
| Some (Unix.WSIGNALED n) ->
|
| Some (Unix.WSIGNALED n) ->
|
||||||
Printf.sprintf "was killed by signal %d before binding %s" n path
|
Printf.sprintf "was killed by %s before binding %s" (signal_name n) path
|
||||||
(* Unreachable without WUNTRACED, and here only for exhaustiveness. *)
|
(* Unreachable without WUNTRACED, and here only for exhaustiveness. *)
|
||||||
| Some (Unix.WSTOPPED n) ->
|
| Some (Unix.WSTOPPED n) ->
|
||||||
Printf.sprintf "stopped on signal %d without binding %s" n path
|
Printf.sprintf "stopped on %s without binding %s" (signal_name n) path
|
||||||
| None ->
|
| None ->
|
||||||
Printf.sprintf
|
Printf.sprintf
|
||||||
"was still running after %ds without binding %s, so it was the \
|
"was still running after %ds without binding %s, so it was the \
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user