diff options
| author | Noam Postavsky | 2019-04-08 17:57:22 -0400 |
|---|---|---|
| committer | Noam Postavsky | 2019-04-14 22:43:38 -0400 |
| commit | 9800df69cb7003bda1f2b98d6f11e89ba95afb9b (patch) | |
| tree | 8de5b8b10e6a0743b7f227bef6bf87ffdb98ed4a | |
| parent | fc0f469fb5b5eb28ca4d9948190be6cb1bd8156e (diff) | |
| download | emacs-9800df69cb7003bda1f2b98d6f11e89ba95afb9b.tar.gz emacs-9800df69cb7003bda1f2b98d6f11e89ba95afb9b.zip | |
Let debugger handle process spawn errors on w32 (Bug#33016)
Since child_setup() is called between block_input()...unblock_input(),
when an error is signaled the Lisp debugger is prevented from
starting. Therefore, let the callers signal the error instead (which
they already do for non-w32 platforms, just the error message needs an
update).
* src/callproc.c (child_setup) [WINDOWSNT]: Don't call
report_file_error here.
(call_process) [WINDOWNT]:
* src/process.c (create_process) [WINDOWSNT]: Call report_file_errno
here instead, after the unblock_input() call, same as for !WINDOWSNT.
* src/lisp.h (CHILD_SETUP_ERROR_DESC): New preprocessor define. Flip
the containing ifndef DOS_NT branches so that it's ifdef DOS_NT.
* src/eval.c (when_entered_debugger): Remove.
(syms_of_eval) <internal-when-entered-debugger>: Define it as a Lisp
integer variable instead.
(maybe_call_debugger): Update comment.
* test/src/process-tests.el (make-process-w32-debug-spawn-error):
* test/src/callproc-tests.el (call-process-w32-debug-spawn-error): New
tests.
| -rw-r--r-- | src/callproc.c | 7 | ||||
| -rw-r--r-- | src/eval.c | 24 | ||||
| -rw-r--r-- | src/lisp.h | 9 | ||||
| -rw-r--r-- | src/process.c | 2 | ||||
| -rw-r--r-- | test/src/callproc-tests.el | 22 | ||||
| -rw-r--r-- | test/src/process-tests.el | 20 |
6 files changed, 65 insertions, 19 deletions
diff --git a/src/callproc.c b/src/callproc.c index a3d09609d7b..2cdf84d9a80 100644 --- a/src/callproc.c +++ b/src/callproc.c | |||
| @@ -681,7 +681,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, | |||
| 681 | unblock_input (); | 681 | unblock_input (); |
| 682 | 682 | ||
| 683 | if (pid < 0) | 683 | if (pid < 0) |
| 684 | report_file_errno ("Doing vfork", Qnil, child_errno); | 684 | report_file_errno (CHILD_SETUP_ERROR_DESC, Qnil, child_errno); |
| 685 | 685 | ||
| 686 | /* Close our file descriptors, except for callproc_fd[CALLPROC_PIPEREAD] | 686 | /* Close our file descriptors, except for callproc_fd[CALLPROC_PIPEREAD] |
| 687 | since we will use that to read input from. */ | 687 | since we will use that to read input from. */ |
| @@ -1174,7 +1174,7 @@ exec_failed (char const *name, int err) | |||
| 1174 | executable directory by the parent. | 1174 | executable directory by the parent. |
| 1175 | 1175 | ||
| 1176 | On GNUish hosts, either exec or return an error number. | 1176 | On GNUish hosts, either exec or return an error number. |
| 1177 | On MS-Windows, either return a pid or signal an error. | 1177 | On MS-Windows, either return a pid or return -1 and set errno. |
| 1178 | On MS-DOS, either return an exit status or signal an error. */ | 1178 | On MS-DOS, either return an exit status or signal an error. */ |
| 1179 | 1179 | ||
| 1180 | CHILD_SETUP_TYPE | 1180 | CHILD_SETUP_TYPE |
| @@ -1319,9 +1319,6 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, | |||
| 1319 | /* Spawn the child. (See w32proc.c:sys_spawnve). */ | 1319 | /* Spawn the child. (See w32proc.c:sys_spawnve). */ |
| 1320 | cpid = spawnve (_P_NOWAIT, new_argv[0], new_argv, env); | 1320 | cpid = spawnve (_P_NOWAIT, new_argv[0], new_argv, env); |
| 1321 | reset_standard_handles (in, out, err, handles); | 1321 | reset_standard_handles (in, out, err, handles); |
| 1322 | if (cpid == -1) | ||
| 1323 | /* An error occurred while trying to spawn the process. */ | ||
| 1324 | report_file_error ("Spawning child process", Qnil); | ||
| 1325 | return cpid; | 1322 | return cpid; |
| 1326 | 1323 | ||
| 1327 | #else /* not WINDOWSNT */ | 1324 | #else /* not WINDOWSNT */ |
diff --git a/src/eval.c b/src/eval.c index e9f118c5cb9..fa7b2d06031 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -52,15 +52,6 @@ Lisp_Object Vautoload_queue; | |||
| 52 | is shutting down. */ | 52 | is shutting down. */ |
| 53 | Lisp_Object Vrun_hooks; | 53 | Lisp_Object Vrun_hooks; |
| 54 | 54 | ||
| 55 | /* The value of num_nonmacro_input_events as of the last time we | ||
| 56 | started to enter the debugger. If we decide to enter the debugger | ||
| 57 | again when this is still equal to num_nonmacro_input_events, then we | ||
| 58 | know that the debugger itself has an error, and we should just | ||
| 59 | signal the error instead of entering an infinite loop of debugger | ||
| 60 | invocations. */ | ||
| 61 | |||
| 62 | static intmax_t when_entered_debugger; | ||
| 63 | |||
| 64 | /* The function from which the last `signal' was called. Set in | 55 | /* The function from which the last `signal' was called. Set in |
| 65 | Fsignal. */ | 56 | Fsignal. */ |
| 66 | /* FIXME: We should probably get rid of this! */ | 57 | /* FIXME: We should probably get rid of this! */ |
| @@ -1835,7 +1826,8 @@ maybe_call_debugger (Lisp_Object conditions, Lisp_Object sig, Lisp_Object data) | |||
| 1835 | ? debug_on_quit | 1826 | ? debug_on_quit |
| 1836 | : wants_debugger (Vdebug_on_error, conditions)) | 1827 | : wants_debugger (Vdebug_on_error, conditions)) |
| 1837 | && ! skip_debugger (conditions, combined_data) | 1828 | && ! skip_debugger (conditions, combined_data) |
| 1838 | /* RMS: What's this for? */ | 1829 | /* See commentary on definition of |
| 1830 | `internal-when-entered-debugger'. */ | ||
| 1839 | && when_entered_debugger < num_nonmacro_input_events) | 1831 | && when_entered_debugger < num_nonmacro_input_events) |
| 1840 | { | 1832 | { |
| 1841 | call_debugger (list2 (Qerror, combined_data)); | 1833 | call_debugger (list2 (Qerror, combined_data)); |
| @@ -4170,6 +4162,18 @@ Note that `debug-on-error', `debug-on-quit' and friends | |||
| 4170 | still determine whether to handle the particular condition. */); | 4162 | still determine whether to handle the particular condition. */); |
| 4171 | Vdebug_on_signal = Qnil; | 4163 | Vdebug_on_signal = Qnil; |
| 4172 | 4164 | ||
| 4165 | /* The value of num_nonmacro_input_events as of the last time we | ||
| 4166 | started to enter the debugger. If we decide to enter the debugger | ||
| 4167 | again when this is still equal to num_nonmacro_input_events, then we | ||
| 4168 | know that the debugger itself has an error, and we should just | ||
| 4169 | signal the error instead of entering an infinite loop of debugger | ||
| 4170 | invocations. */ | ||
| 4171 | DEFSYM (Qinternal_when_entered_debugger, "internal-when-entered-debugger"); | ||
| 4172 | DEFVAR_INT ("internal-when-entered-debugger", when_entered_debugger, | ||
| 4173 | doc: /* The number of keyboard events as of last time `debugger' was called. | ||
| 4174 | Used to avoid infinite loops if the debugger itself has an error. | ||
| 4175 | Don't set this unless you're sure that can't happen. */); | ||
| 4176 | |||
| 4173 | /* When lexical binding is being used, | 4177 | /* When lexical binding is being used, |
| 4174 | Vinternal_interpreter_environment is non-nil, and contains an alist | 4178 | Vinternal_interpreter_environment is non-nil, and contains an alist |
| 4175 | of lexically-bound variable, or (t), indicating an empty | 4179 | of lexically-bound variable, or (t), indicating an empty |
diff --git a/src/lisp.h b/src/lisp.h index 681efc3b52b..2915944ffec 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4480,11 +4480,14 @@ extern void syms_of_process (void); | |||
| 4480 | extern void setup_process_coding_systems (Lisp_Object); | 4480 | extern void setup_process_coding_systems (Lisp_Object); |
| 4481 | 4481 | ||
| 4482 | /* Defined in callproc.c. */ | 4482 | /* Defined in callproc.c. */ |
| 4483 | #ifndef DOS_NT | 4483 | #ifdef DOS_NT |
| 4484 | # define CHILD_SETUP_TYPE _Noreturn void | ||
| 4485 | #else | ||
| 4486 | # define CHILD_SETUP_TYPE int | 4484 | # define CHILD_SETUP_TYPE int |
| 4485 | # define CHILD_SETUP_ERROR_DESC "Spawning child process" | ||
| 4486 | #else | ||
| 4487 | # define CHILD_SETUP_TYPE _Noreturn void | ||
| 4488 | # define CHILD_SETUP_ERROR_DESC "Doing vfork" | ||
| 4487 | #endif | 4489 | #endif |
| 4490 | |||
| 4488 | extern CHILD_SETUP_TYPE child_setup (int, int, int, char **, bool, Lisp_Object); | 4491 | extern CHILD_SETUP_TYPE child_setup (int, int, int, char **, bool, Lisp_Object); |
| 4489 | extern void init_callproc_1 (void); | 4492 | extern void init_callproc_1 (void); |
| 4490 | extern void init_callproc (void); | 4493 | extern void init_callproc (void); |
diff --git a/src/process.c b/src/process.c index 6770a5ed884..0c440371628 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -2233,7 +2233,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 2233 | unblock_input (); | 2233 | unblock_input (); |
| 2234 | 2234 | ||
| 2235 | if (pid < 0) | 2235 | if (pid < 0) |
| 2236 | report_file_errno ("Doing vfork", Qnil, vfork_errno); | 2236 | report_file_errno (CHILD_SETUP_ERROR_DESC, Qnil, vfork_errno); |
| 2237 | else | 2237 | else |
| 2238 | { | 2238 | { |
| 2239 | /* vfork succeeded. */ | 2239 | /* vfork succeeded. */ |
diff --git a/test/src/callproc-tests.el b/test/src/callproc-tests.el index 7b30a251cce..f351b6e2148 100644 --- a/test/src/callproc-tests.el +++ b/test/src/callproc-tests.el | |||
| @@ -37,3 +37,25 @@ | |||
| 37 | (split-string-and-unquote (buffer-string))) | 37 | (split-string-and-unquote (buffer-string))) |
| 38 | (should (equal initial-shell "nil")) | 38 | (should (equal initial-shell "nil")) |
| 39 | (should-not (equal initial-shell shell)))) | 39 | (should-not (equal initial-shell shell)))) |
| 40 | |||
| 41 | (ert-deftest call-process-w32-debug-spawn-error () | ||
| 42 | "Check that debugger runs on `call-process' failure (Bug#33016)." | ||
| 43 | (skip-unless (eq system-type 'windows-nt)) | ||
| 44 | (let* ((debug-on-error t) | ||
| 45 | (have-called-debugger nil) | ||
| 46 | (debugger (lambda (&rest _) | ||
| 47 | (setq have-called-debugger t) | ||
| 48 | ;; Allow entering the debugger later in the same | ||
| 49 | ;; test run, before going back to the command | ||
| 50 | ;; loop. | ||
| 51 | (setq internal-when-entered-debugger -1)))) | ||
| 52 | (should (eq :got-error ;; NOTE: `should-error' would inhibit debugger. | ||
| 53 | (condition-case-unless-debug () | ||
| 54 | ;; On Windows, "nul.FOO" act like an always-empty | ||
| 55 | ;; file for any FOO, in any directory. So this | ||
| 56 | ;; passes Emacs' test for the file's existence, | ||
| 57 | ;; and ensures we hit an error in the w32 process | ||
| 58 | ;; spawn code. | ||
| 59 | (call-process "c:/nul.exe") | ||
| 60 | (error :got-error)))) | ||
| 61 | (should have-called-debugger))) | ||
diff --git a/test/src/process-tests.el b/test/src/process-tests.el index 5dbf441e8c2..0bb7ebe50a8 100644 --- a/test/src/process-tests.el +++ b/test/src/process-tests.el | |||
| @@ -215,6 +215,26 @@ | |||
| 215 | (string-to-list "stdout\n") | 215 | (string-to-list "stdout\n") |
| 216 | (string-to-list "stderr\n")))))) | 216 | (string-to-list "stderr\n")))))) |
| 217 | 217 | ||
| 218 | (ert-deftest make-process-w32-debug-spawn-error () | ||
| 219 | "Check that debugger runs on `make-process' failure (Bug#33016)." | ||
| 220 | (skip-unless (eq system-type 'windows-nt)) | ||
| 221 | (let* ((debug-on-error t) | ||
| 222 | (have-called-debugger nil) | ||
| 223 | (debugger (lambda (&rest _) | ||
| 224 | (setq have-called-debugger t) | ||
| 225 | ;; Allow entering the debugger later in the same | ||
| 226 | ;; test run, before going back to the command | ||
| 227 | ;; loop. | ||
| 228 | (setq internal-when-entered-debugger -1)))) | ||
| 229 | (should (eq :got-error ;; NOTE: `should-error' would inhibit debugger. | ||
| 230 | (condition-case-unless-debug () | ||
| 231 | ;; Emacs doesn't search for absolute filenames, so | ||
| 232 | ;; the error will be hit in the w32 process spawn | ||
| 233 | ;; code. | ||
| 234 | (make-process :name "test" :command '("c:/No-Such-Command")) | ||
| 235 | (error :got-error)))) | ||
| 236 | (should have-called-debugger))) | ||
| 237 | |||
| 218 | (ert-deftest make-process/file-handler/found () | 238 | (ert-deftest make-process/file-handler/found () |
| 219 | "Check that the ‘:file-handler’ argument of ‘make-process’ | 239 | "Check that the ‘:file-handler’ argument of ‘make-process’ |
| 220 | works as expected if a file name handler is found." | 240 | works as expected if a file name handler is found." |