mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-10 02:18:46 +00:00
8352533: Report useful IOExceptions when jspawnhelper fails
Reviewed-by: simonis, rriggs, stuefe
This commit is contained in:
parent
86f39ab5bb
commit
5c73dfc28c
@ -317,12 +317,23 @@ releaseBytes(JNIEnv *env, jbyteArray arr, const char* parr)
|
||||
(*env)->ReleaseByteArrayElements(env, arr, (jbyte*) parr, JNI_ABORT);
|
||||
}
|
||||
|
||||
#define IOE_FORMAT "error=%d, %s"
|
||||
#define IOE_FORMAT "%s, error: %d (%s) %s"
|
||||
|
||||
#define SPAWN_HELPER_INTERNAL_ERROR_MSG "\n" \
|
||||
"Possible reasons:\n" \
|
||||
" - Spawn helper ran into JDK version mismatch\n" \
|
||||
" - Spawn helper ran into unexpected internal error\n" \
|
||||
" - Spawn helper was terminated by another process\n" \
|
||||
"Possible solutions:\n" \
|
||||
" - Restart JVM, especially after in-place JDK updates\n" \
|
||||
" - Check system logs for JDK-related errors\n" \
|
||||
" - Re-install JDK to fix permission/versioning problems\n" \
|
||||
" - Switch to legacy launch mechanism with -Djdk.lang.Process.launchMechanism=VFORK\n"
|
||||
|
||||
static void
|
||||
throwIOException(JNIEnv *env, int errnum, const char *defaultDetail)
|
||||
throwIOExceptionImpl(JNIEnv *env, int errnum, const char *externalDetail, const char *internalDetail)
|
||||
{
|
||||
const char *detail = defaultDetail;
|
||||
const char *errorDetail;
|
||||
char *errmsg;
|
||||
size_t fmtsize;
|
||||
char tmpbuf[1024];
|
||||
@ -330,16 +341,22 @@ throwIOException(JNIEnv *env, int errnum, const char *defaultDetail)
|
||||
|
||||
if (errnum != 0) {
|
||||
int ret = getErrorString(errnum, tmpbuf, sizeof(tmpbuf));
|
||||
if (ret != EINVAL)
|
||||
detail = tmpbuf;
|
||||
if (ret != EINVAL) {
|
||||
errorDetail = tmpbuf;
|
||||
} else {
|
||||
errorDetail = "unknown";
|
||||
}
|
||||
} else {
|
||||
errorDetail = "none";
|
||||
}
|
||||
|
||||
/* ASCII Decimal representation uses 2.4 times as many bits as binary. */
|
||||
fmtsize = sizeof(IOE_FORMAT) + strlen(detail) + 3 * sizeof(errnum);
|
||||
fmtsize = sizeof(IOE_FORMAT) + strlen(externalDetail) + 3 * sizeof(errnum) + strlen(errorDetail) + strlen(internalDetail) + 1;
|
||||
errmsg = NEW(char, fmtsize);
|
||||
if (errmsg == NULL)
|
||||
return;
|
||||
|
||||
snprintf(errmsg, fmtsize, IOE_FORMAT, errnum, detail);
|
||||
snprintf(errmsg, fmtsize, IOE_FORMAT, externalDetail, errnum, errorDetail, internalDetail);
|
||||
s = JNU_NewStringPlatform(env, errmsg);
|
||||
if (s != NULL) {
|
||||
jobject x = JNU_NewObjectByName(env, "java/io/IOException",
|
||||
@ -350,14 +367,38 @@ throwIOException(JNIEnv *env, int errnum, const char *defaultDetail)
|
||||
free(errmsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IOException that signifies an internal error, e.g. spawn helper failure.
|
||||
*/
|
||||
static void
|
||||
throwInternalIOException(JNIEnv *env, int errnum, const char *externalDetail, int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case MODE_POSIX_SPAWN:
|
||||
throwIOExceptionImpl(env, errnum, externalDetail, SPAWN_HELPER_INTERNAL_ERROR_MSG);
|
||||
break;
|
||||
default:
|
||||
throwIOExceptionImpl(env, errnum, externalDetail, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IOException that signifies a normal error.
|
||||
*/
|
||||
static void
|
||||
throwIOException(JNIEnv *env, int errnum, const char *externalDetail)
|
||||
{
|
||||
throwIOExceptionImpl(env, errnum, externalDetail, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an IOException with a message composed from the result of waitpid status.
|
||||
*/
|
||||
static void throwExitCause(JNIEnv *env, int pid, int status) {
|
||||
static void throwExitCause(JNIEnv *env, int pid, int status, int mode) {
|
||||
char ebuf[128];
|
||||
if (WIFEXITED(status)) {
|
||||
snprintf(ebuf, sizeof ebuf,
|
||||
"Failed to exec spawn helper: pid: %d, exit value: %d",
|
||||
"Failed to exec spawn helper: pid: %d, exit code: %d",
|
||||
pid, WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
snprintf(ebuf, sizeof ebuf,
|
||||
@ -368,7 +409,7 @@ static void throwExitCause(JNIEnv *env, int pid, int status) {
|
||||
"Failed to exec spawn helper: pid: %d, status: 0x%08x",
|
||||
pid, status);
|
||||
}
|
||||
throwIOException(env, 0, ebuf);
|
||||
throwInternalIOException(env, 0, ebuf, mode);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PROCESS
|
||||
@ -691,7 +732,7 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
|
||||
(fds[2] == -1 && pipe(err) < 0) ||
|
||||
(pipe(childenv) < 0) ||
|
||||
(pipe(fail) < 0)) {
|
||||
throwIOException(env, errno, "Bad file descriptor");
|
||||
throwInternalIOException(env, errno, "Bad file descriptor", c->mode);
|
||||
goto Catch;
|
||||
}
|
||||
c->fds[0] = fds[0];
|
||||
@ -725,13 +766,13 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
|
||||
if (resultPid < 0) {
|
||||
switch (c->mode) {
|
||||
case MODE_VFORK:
|
||||
throwIOException(env, errno, "vfork failed");
|
||||
throwInternalIOException(env, errno, "vfork failed", c->mode);
|
||||
break;
|
||||
case MODE_FORK:
|
||||
throwIOException(env, errno, "fork failed");
|
||||
throwInternalIOException(env, errno, "fork failed", c->mode);
|
||||
break;
|
||||
case MODE_POSIX_SPAWN:
|
||||
throwIOException(env, errno, "posix_spawn failed");
|
||||
throwInternalIOException(env, errno, "posix_spawn failed", c->mode);
|
||||
break;
|
||||
}
|
||||
goto Catch;
|
||||
@ -745,20 +786,21 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
|
||||
{
|
||||
int tmpStatus = 0;
|
||||
int p = waitpid(resultPid, &tmpStatus, 0);
|
||||
throwExitCause(env, p, tmpStatus);
|
||||
throwExitCause(env, p, tmpStatus, c->mode);
|
||||
goto Catch;
|
||||
}
|
||||
case sizeof(errnum):
|
||||
if (errnum != CHILD_IS_ALIVE) {
|
||||
/* This can happen if the spawn helper encounters an error
|
||||
* before or during the handshake with the parent. */
|
||||
throwIOException(env, 0, "Bad code from spawn helper "
|
||||
"(Failed to exec spawn helper)");
|
||||
throwInternalIOException(env, 0,
|
||||
"Bad code from spawn helper (Failed to exec spawn helper)",
|
||||
c->mode);
|
||||
goto Catch;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throwIOException(env, errno, "Read failed");
|
||||
throwInternalIOException(env, errno, "Read failed", c->mode);
|
||||
goto Catch;
|
||||
}
|
||||
}
|
||||
@ -770,7 +812,7 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
|
||||
throwIOException(env, errnum, "Exec failed");
|
||||
goto Catch;
|
||||
default:
|
||||
throwIOException(env, errno, "Read failed");
|
||||
throwInternalIOException(env, errno, "Read failed", c->mode);
|
||||
goto Catch;
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
|
||||
* 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464
|
||||
* 8067796 8224905 8263729 8265173 8272600 8231297 8282219 8285517
|
||||
* 8352533
|
||||
* @key intermittent
|
||||
* @summary Basic tests for Process and Environment Variable code
|
||||
* @modules java.base/java.lang:open
|
||||
@ -86,6 +87,7 @@ public class Basic {
|
||||
/* Used for regex String matching for long error messages */
|
||||
static final String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13)";
|
||||
static final String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2)";
|
||||
static final String SPAWNHELPER_FAILURE_MSG = "(Possible reasons:)";
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds since time given by
|
||||
@ -318,7 +320,9 @@ public class Basic {
|
||||
} catch (IOException e) {
|
||||
String m = e.getMessage();
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, PERMISSION_DENIED_ERROR_MSG))
|
||||
!matches(m, PERMISSION_DENIED_ERROR_MSG))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
@ -428,7 +432,9 @@ public class Basic {
|
||||
} catch (IOException e) {
|
||||
String m = e.getMessage();
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, NO_SUCH_FILE_ERROR_MSG))
|
||||
!matches(m, NO_SUCH_FILE_ERROR_MSG))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
@ -441,7 +447,9 @@ public class Basic {
|
||||
} catch (IOException e) {
|
||||
String m = e.getMessage();
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, NO_SUCH_FILE_ERROR_MSG))
|
||||
!matches(m, NO_SUCH_FILE_ERROR_MSG))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
@ -1978,6 +1986,8 @@ public class Basic {
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, NO_SUCH_FILE_ERROR_MSG))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
@ -1995,6 +2005,8 @@ public class Basic {
|
||||
|| (EnglishUnix.is() &&
|
||||
! matches(m, NO_SUCH_FILE_ERROR_MSG)))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
@ -2011,6 +2023,8 @@ public class Basic {
|
||||
|| (EnglishUnix.is() &&
|
||||
! matches(m, NO_SUCH_FILE_ERROR_MSG)))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
@ -2069,8 +2083,9 @@ public class Basic {
|
||||
op.f();
|
||||
fail();
|
||||
} catch (IOException expected) {
|
||||
check(expected.getMessage()
|
||||
.matches("[Ss]tream [Cc]losed"));
|
||||
String m = expected.getMessage();
|
||||
check(m.matches("[Ss]tream [Cc]losed"));
|
||||
check(!matches(m, SPAWNHELPER_FAILURE_MSG));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2122,10 +2137,14 @@ public class Basic {
|
||||
}
|
||||
equal(-1, r);
|
||||
} catch (IOException ioe) {
|
||||
if (!ioe.getMessage().equals("Stream closed")) {
|
||||
String m = ioe.getMessage();
|
||||
if (!m.equals("Stream closed")) {
|
||||
// BufferedInputStream may throw IOE("Stream closed").
|
||||
unexpected(ioe);
|
||||
}
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG)) {
|
||||
unexpected(ioe);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }}};
|
||||
|
||||
thread.start();
|
||||
@ -2179,6 +2198,9 @@ public class Basic {
|
||||
! (msg.matches(".*Bad file.*") ||
|
||||
msg.matches(".*Stream closed.*")))
|
||||
unexpected(e);
|
||||
if (matches(msg, SPAWNHELPER_FAILURE_MSG)) {
|
||||
unexpected(e);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) { unexpected(t); }}};
|
||||
reader.setDaemon(true);
|
||||
@ -2271,6 +2293,9 @@ public class Basic {
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, PERMISSION_DENIED_ERROR_MSG))
|
||||
unexpected(e);
|
||||
if (matches(m, SPAWNHELPER_FAILURE_MSG)) {
|
||||
unexpected(e);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
new File("emptyCommand").delete();
|
||||
|
||||
@ -49,14 +49,21 @@ public class JspawnhelperProtocol {
|
||||
private static final String[] CMD = { "pwd" };
|
||||
private static final String ENV_KEY = "JTREG_JSPAWNHELPER_PROTOCOL_TEST";
|
||||
|
||||
private static final String SPAWNHELPER_FAILURE_MSG = "Possible reasons:";
|
||||
|
||||
private static void parentCode(String arg) throws IOException, InterruptedException {
|
||||
System.out.println("Recursively executing 'JspawnhelperProtocol " + arg + "'");
|
||||
Process p = null;
|
||||
try {
|
||||
p = Runtime.getRuntime().exec(CMD);
|
||||
} catch (Exception e) {
|
||||
// Check that exception contains rich message on failure.
|
||||
e.printStackTrace(System.out);
|
||||
System.exit(ERROR);
|
||||
if (e instanceof IOException && e.getMessage().contains(SPAWNHELPER_FAILURE_MSG)) {
|
||||
System.exit(ERROR);
|
||||
} else {
|
||||
System.exit(ERROR + 3);
|
||||
}
|
||||
}
|
||||
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
System.out.println("Child process timed out");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user