Skip to content

Commit 361f179

Browse files
rgithubliPaul Hohensee
authored andcommitted
8352533: Report useful IOExceptions when jspawnhelper fails
Backport-of: 5c73dfc28cbd6801ac85c6685fb8c77aad3ab0b7
1 parent d4b91ad commit 361f179

File tree

3 files changed

+100
-26
lines changed

3 files changed

+100
-26
lines changed

src/java.base/unix/native/libjava/ProcessImpl_md.c

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -318,29 +318,46 @@ releaseBytes(JNIEnv *env, jbyteArray arr, const char* parr)
318318
(*env)->ReleaseByteArrayElements(env, arr, (jbyte*) parr, JNI_ABORT);
319319
}
320320

321-
#define IOE_FORMAT "error=%d, %s"
321+
#define IOE_FORMAT "%s, error: %d (%s) %s"
322+
323+
#define SPAWN_HELPER_INTERNAL_ERROR_MSG "\n" \
324+
"Possible reasons:\n" \
325+
" - Spawn helper ran into JDK version mismatch\n" \
326+
" - Spawn helper ran into unexpected internal error\n" \
327+
" - Spawn helper was terminated by another process\n" \
328+
"Possible solutions:\n" \
329+
" - Restart JVM, especially after in-place JDK updates\n" \
330+
" - Check system logs for JDK-related errors\n" \
331+
" - Re-install JDK to fix permission/versioning problems\n" \
332+
" - Switch to legacy launch mechanism with -Djdk.lang.Process.launchMechanism=VFORK\n"
322333

323334
static void
324-
throwIOException(JNIEnv *env, int errnum, const char *defaultDetail)
335+
throwIOExceptionImpl(JNIEnv *env, int errnum, const char *externalDetail, const char *internalDetail)
325336
{
326-
const char *detail = defaultDetail;
337+
const char *errorDetail;
327338
char *errmsg;
328339
size_t fmtsize;
329340
char tmpbuf[1024];
330341
jstring s;
331342

332343
if (errnum != 0) {
333344
int ret = getErrorString(errnum, tmpbuf, sizeof(tmpbuf));
334-
if (ret != EINVAL)
335-
detail = tmpbuf;
345+
if (ret != EINVAL) {
346+
errorDetail = tmpbuf;
347+
} else {
348+
errorDetail = "unknown";
349+
}
350+
} else {
351+
errorDetail = "none";
336352
}
353+
337354
/* ASCII Decimal representation uses 2.4 times as many bits as binary. */
338-
fmtsize = sizeof(IOE_FORMAT) + strlen(detail) + 3 * sizeof(errnum);
355+
fmtsize = sizeof(IOE_FORMAT) + strlen(externalDetail) + 3 * sizeof(errnum) + strlen(errorDetail) + strlen(internalDetail) + 1;
339356
errmsg = NEW(char, fmtsize);
340357
if (errmsg == NULL)
341358
return;
342359

343-
snprintf(errmsg, fmtsize, IOE_FORMAT, errnum, detail);
360+
snprintf(errmsg, fmtsize, IOE_FORMAT, externalDetail, errnum, errorDetail, internalDetail);
344361
s = JNU_NewStringPlatform(env, errmsg);
345362
if (s != NULL) {
346363
jobject x = JNU_NewObjectByName(env, "java/io/IOException",
@@ -351,14 +368,38 @@ throwIOException(JNIEnv *env, int errnum, const char *defaultDetail)
351368
free(errmsg);
352369
}
353370

371+
/**
372+
* Throws IOException that signifies an internal error, e.g. spawn helper failure.
373+
*/
374+
static void
375+
throwInternalIOException(JNIEnv *env, int errnum, const char *externalDetail, int mode)
376+
{
377+
switch (mode) {
378+
case MODE_POSIX_SPAWN:
379+
throwIOExceptionImpl(env, errnum, externalDetail, SPAWN_HELPER_INTERNAL_ERROR_MSG);
380+
break;
381+
default:
382+
throwIOExceptionImpl(env, errnum, externalDetail, "");
383+
}
384+
}
385+
386+
/**
387+
* Throws IOException that signifies a normal error.
388+
*/
389+
static void
390+
throwIOException(JNIEnv *env, int errnum, const char *externalDetail)
391+
{
392+
throwIOExceptionImpl(env, errnum, externalDetail, "");
393+
}
394+
354395
/**
355396
* Throws an IOException with a message composed from the result of waitpid status.
356397
*/
357-
static void throwExitCause(JNIEnv *env, int pid, int status) {
398+
static void throwExitCause(JNIEnv *env, int pid, int status, int mode) {
358399
char ebuf[128];
359400
if (WIFEXITED(status)) {
360401
snprintf(ebuf, sizeof ebuf,
361-
"Failed to exec spawn helper: pid: %d, exit value: %d",
402+
"Failed to exec spawn helper: pid: %d, exit code: %d",
362403
pid, WEXITSTATUS(status));
363404
} else if (WIFSIGNALED(status)) {
364405
snprintf(ebuf, sizeof ebuf,
@@ -369,7 +410,7 @@ static void throwExitCause(JNIEnv *env, int pid, int status) {
369410
"Failed to exec spawn helper: pid: %d, status: 0x%08x",
370411
pid, status);
371412
}
372-
throwIOException(env, 0, ebuf);
413+
throwInternalIOException(env, 0, ebuf, mode);
373414
}
374415

375416
#ifdef DEBUG_PROCESS
@@ -692,7 +733,7 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
692733
(fds[2] == -1 && pipe(err) < 0) ||
693734
(pipe(childenv) < 0) ||
694735
(pipe(fail) < 0)) {
695-
throwIOException(env, errno, "Bad file descriptor");
736+
throwInternalIOException(env, errno, "Bad file descriptor", c->mode);
696737
goto Catch;
697738
}
698739
c->fds[0] = fds[0];
@@ -726,13 +767,13 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
726767
if (resultPid < 0) {
727768
switch (c->mode) {
728769
case MODE_VFORK:
729-
throwIOException(env, errno, "vfork failed");
770+
throwInternalIOException(env, errno, "vfork failed", c->mode);
730771
break;
731772
case MODE_FORK:
732-
throwIOException(env, errno, "fork failed");
773+
throwInternalIOException(env, errno, "fork failed", c->mode);
733774
break;
734775
case MODE_POSIX_SPAWN:
735-
throwIOException(env, errno, "posix_spawn failed");
776+
throwInternalIOException(env, errno, "posix_spawn failed", c->mode);
736777
break;
737778
}
738779
goto Catch;
@@ -746,20 +787,21 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
746787
{
747788
int tmpStatus = 0;
748789
int p = waitpid(resultPid, &tmpStatus, 0);
749-
throwExitCause(env, p, tmpStatus);
790+
throwExitCause(env, p, tmpStatus, c->mode);
750791
goto Catch;
751792
}
752793
case sizeof(errnum):
753794
if (errnum != CHILD_IS_ALIVE) {
754795
/* This can happen if the spawn helper encounters an error
755796
* before or during the handshake with the parent. */
756-
throwIOException(env, 0, "Bad code from spawn helper "
757-
"(Failed to exec spawn helper)");
797+
throwInternalIOException(env, 0,
798+
"Bad code from spawn helper (Failed to exec spawn helper)",
799+
c->mode);
758800
goto Catch;
759801
}
760802
break;
761803
default:
762-
throwIOException(env, errno, "Read failed");
804+
throwInternalIOException(env, errno, "Read failed", c->mode);
763805
goto Catch;
764806
}
765807
}
@@ -771,7 +813,7 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
771813
throwIOException(env, errnum, "Exec failed");
772814
goto Catch;
773815
default:
774-
throwIOException(env, errno, "Read failed");
816+
throwInternalIOException(env, errno, "Read failed", c->mode);
775817
goto Catch;
776818
}
777819

test/jdk/java/lang/ProcessBuilder/Basic.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
2929
* 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464
3030
* 8067796 8224905 8263729 8265173 8272600 8231297 8282219 8285517
31+
* 8352533
3132
* @key intermittent
3233
* @summary Basic tests for Process and Environment Variable code
3334
* @modules java.base/java.lang:open
@@ -89,6 +90,7 @@ public class Basic {
8990
/* Used for regex String matching for long error messages */
9091
static final String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13)";
9192
static final String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2)";
93+
static final String SPAWNHELPER_FAILURE_MSG = "(Possible reasons:)";
9294

9395
/**
9496
* Returns the number of milliseconds since time given by
@@ -321,7 +323,9 @@ static void checkPermissionDenied(ProcessBuilder pb) {
321323
} catch (IOException e) {
322324
String m = e.getMessage();
323325
if (EnglishUnix.is() &&
324-
! matches(m, PERMISSION_DENIED_ERROR_MSG))
326+
!matches(m, PERMISSION_DENIED_ERROR_MSG))
327+
unexpected(e);
328+
if (matches(m, SPAWNHELPER_FAILURE_MSG))
325329
unexpected(e);
326330
} catch (Throwable t) { unexpected(t); }
327331
}
@@ -431,7 +435,9 @@ public static void main(String args[]) throws Throwable {
431435
} catch (IOException e) {
432436
String m = e.getMessage();
433437
if (EnglishUnix.is() &&
434-
! matches(m, NO_SUCH_FILE_ERROR_MSG))
438+
!matches(m, NO_SUCH_FILE_ERROR_MSG))
439+
unexpected(e);
440+
if (matches(m, SPAWNHELPER_FAILURE_MSG))
435441
unexpected(e);
436442
} catch (Throwable t) { unexpected(t); }
437443

@@ -444,7 +450,9 @@ public static void main(String args[]) throws Throwable {
444450
} catch (IOException e) {
445451
String m = e.getMessage();
446452
if (EnglishUnix.is() &&
447-
! matches(m, NO_SUCH_FILE_ERROR_MSG))
453+
!matches(m, NO_SUCH_FILE_ERROR_MSG))
454+
unexpected(e);
455+
if (matches(m, SPAWNHELPER_FAILURE_MSG))
448456
unexpected(e);
449457
} catch (Throwable t) { unexpected(t); }
450458

@@ -2052,6 +2060,8 @@ public void doIt(Map<String,String> environ) {
20522060
if (EnglishUnix.is() &&
20532061
! matches(m, NO_SUCH_FILE_ERROR_MSG))
20542062
unexpected(e);
2063+
if (matches(m, SPAWNHELPER_FAILURE_MSG))
2064+
unexpected(e);
20552065
} catch (Throwable t) { unexpected(t); }
20562066

20572067
//----------------------------------------------------------------
@@ -2069,6 +2079,8 @@ public void doIt(Map<String,String> environ) {
20692079
|| (EnglishUnix.is() &&
20702080
! matches(m, NO_SUCH_FILE_ERROR_MSG)))
20712081
unexpected(e);
2082+
if (matches(m, SPAWNHELPER_FAILURE_MSG))
2083+
unexpected(e);
20722084
} catch (Throwable t) { unexpected(t); }
20732085

20742086
//----------------------------------------------------------------
@@ -2085,6 +2097,8 @@ public void doIt(Map<String,String> environ) {
20852097
|| (EnglishUnix.is() &&
20862098
! matches(m, NO_SUCH_FILE_ERROR_MSG)))
20872099
unexpected(e);
2100+
if (matches(m, SPAWNHELPER_FAILURE_MSG))
2101+
unexpected(e);
20882102
} catch (Throwable t) { unexpected(t); }
20892103

20902104
//----------------------------------------------------------------
@@ -2143,8 +2157,9 @@ public void doIt(Map<String,String> environ) {
21432157
op.f();
21442158
fail();
21452159
} catch (IOException expected) {
2146-
check(expected.getMessage()
2147-
.matches("[Ss]tream [Cc]losed"));
2160+
String m = expected.getMessage();
2161+
check(m.matches("[Ss]tream [Cc]losed"));
2162+
check(!matches(m, SPAWNHELPER_FAILURE_MSG));
21482163
}
21492164
}
21502165
}
@@ -2196,10 +2211,14 @@ public void run() {
21962211
}
21972212
equal(-1, r);
21982213
} catch (IOException ioe) {
2199-
if (!ioe.getMessage().equals("Stream closed")) {
2214+
String m = ioe.getMessage();
2215+
if (!m.equals("Stream closed")) {
22002216
// BufferedInputStream may throw IOE("Stream closed").
22012217
unexpected(ioe);
22022218
}
2219+
if (matches(m, SPAWNHELPER_FAILURE_MSG)) {
2220+
unexpected(ioe);
2221+
}
22032222
} catch (Throwable t) { unexpected(t); }}};
22042223

22052224
thread.start();
@@ -2253,6 +2272,9 @@ public void run() {
22532272
! (msg.matches(".*Bad file.*") ||
22542273
msg.matches(".*Stream closed.*")))
22552274
unexpected(e);
2275+
if (matches(msg, SPAWNHELPER_FAILURE_MSG)) {
2276+
unexpected(e);
2277+
}
22562278
}
22572279
catch (Throwable t) { unexpected(t); }}};
22582280
reader.setDaemon(true);
@@ -2345,6 +2367,9 @@ public void run() {
23452367
if (EnglishUnix.is() &&
23462368
! matches(m, PERMISSION_DENIED_ERROR_MSG))
23472369
unexpected(e);
2370+
if (matches(m, SPAWNHELPER_FAILURE_MSG)) {
2371+
unexpected(e);
2372+
}
23482373
} catch (Throwable t) { unexpected(t); }
23492374

23502375
new File("emptyCommand").delete();

test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,21 @@ public class JspawnhelperProtocol {
4949
private static final String[] CMD = { "pwd" };
5050
private static final String ENV_KEY = "JTREG_JSPAWNHELPER_PROTOCOL_TEST";
5151

52+
private static final String SPAWNHELPER_FAILURE_MSG = "Possible reasons:";
53+
5254
private static void parentCode(String arg) throws IOException, InterruptedException {
5355
System.out.println("Recursively executing 'JspawnhelperProtocol " + arg + "'");
5456
Process p = null;
5557
try {
5658
p = Runtime.getRuntime().exec(CMD);
5759
} catch (Exception e) {
60+
// Check that exception contains rich message on failure.
5861
e.printStackTrace(System.out);
59-
System.exit(ERROR);
62+
if (e instanceof IOException && e.getMessage().contains(SPAWNHELPER_FAILURE_MSG)) {
63+
System.exit(ERROR);
64+
} else {
65+
System.exit(ERROR + 3);
66+
}
6067
}
6168
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
6269
System.out.println("Child process timed out");

0 commit comments

Comments
 (0)