aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Postavsky2019-04-08 17:57:22 -0400
committerNoam Postavsky2019-04-14 22:43:38 -0400
commit9800df69cb7003bda1f2b98d6f11e89ba95afb9b (patch)
tree8de5b8b10e6a0743b7f227bef6bf87ffdb98ed4a
parentfc0f469fb5b5eb28ca4d9948190be6cb1bd8156e (diff)
downloademacs-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.c7
-rw-r--r--src/eval.c24
-rw-r--r--src/lisp.h9
-rw-r--r--src/process.c2
-rw-r--r--test/src/callproc-tests.el22
-rw-r--r--test/src/process-tests.el20
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
1180CHILD_SETUP_TYPE 1180CHILD_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. */
53Lisp_Object Vrun_hooks; 53Lisp_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
62static 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
4170still determine whether to handle the particular condition. */); 4162still 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.
4174Used to avoid infinite loops if the debugger itself has an error.
4175Don'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);
4480extern void setup_process_coding_systems (Lisp_Object); 4480extern 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
4488extern CHILD_SETUP_TYPE child_setup (int, int, int, char **, bool, Lisp_Object); 4491extern CHILD_SETUP_TYPE child_setup (int, int, int, char **, bool, Lisp_Object);
4489extern void init_callproc_1 (void); 4492extern void init_callproc_1 (void);
4490extern void init_callproc (void); 4493extern 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’
220works as expected if a file name handler is found." 240works as expected if a file name handler is found."