Skip to content

Commit cf940e1

Browse files
author
Doug Simon
committed
8335553: [Graal] Compiler thread calls into jdk.internal.vm.VMSupport.decodeAndThrowThrowable and crashes in OOM situation
Reviewed-by: yzheng, never, dholmes
1 parent b363de8 commit cf940e1

File tree

6 files changed

+81
-41
lines changed

6 files changed

+81
-41
lines changed

src/hotspot/share/jvmci/jvmciCompilerToVM.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,35 @@ C2V_VMENTRY_NULL(jobject, lookupConstantInPool, (JNIEnv* env, jobject, ARGUMENT_
757757
return JVMCIENV->get_jobject(result);
758758
}
759759
}
760+
#ifdef ASSERT
761+
// Support for testing an OOME raised in a context where the current thread cannot call Java
762+
// 1. Put -Dtest.jvmci.oome_in_lookupConstantInPool=<trace> on the command line to
763+
// discover possible values for step 2.
764+
// Example output:
765+
//
766+
// CompilerToVM.lookupConstantInPool: "Overflow: String length out of range"{0x00000007ffeb2960}
767+
// CompilerToVM.lookupConstantInPool: "null"{0x00000007ffebdfe8}
768+
// CompilerToVM.lookupConstantInPool: "Maximum lock count exceeded"{0x00000007ffec4f90}
769+
// CompilerToVM.lookupConstantInPool: "Negative length"{0x00000007ffec4468}
770+
//
771+
// 2. Choose a value shown in step 1.
772+
// Example: -Dtest.jvmci.oome_in_lookupConstantInPool=Negative
773+
const char* val = Arguments::PropertyList_get_value(Arguments::system_properties(), "test.jvmci.oome_in_lookupConstantInPool");
774+
if (val != nullptr) {
775+
const char* str = obj->print_value_string();
776+
if (strstr(val, "<trace>") != nullptr) {
777+
tty->print_cr("CompilerToVM.lookupConstantInPool: %s", str);
778+
} else if (strstr(str, val) != nullptr) {
779+
Handle garbage;
780+
while (true) {
781+
// Trigger an OutOfMemoryError
782+
objArrayOop next = oopFactory::new_objectArray(0x7FFFFFFF, CHECK_NULL);
783+
next->obj_at_put(0, garbage());
784+
garbage = Handle(THREAD, next);
785+
}
786+
}
787+
}
788+
#endif
760789
return JVMCIENV->get_jobject(JVMCIENV->get_object_constant(obj));
761790
C2V_END
762791

src/hotspot/share/jvmci/jvmciEnv.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,11 @@ class ExceptionTranslation: public StackObj {
399399
_encoded_ok = 0, // exception was successfully encoded into buffer
400400
_buffer_alloc_fail = 1, // native memory for buffer could not be allocated
401401
_encode_oome_fail = 2, // OutOfMemoryError thrown during encoding
402-
_encode_fail = 3 // some other problem occured during encoding. If buffer != 0,
402+
_encode_fail = 3, // some other problem occured during encoding. If buffer != 0,
403403
// buffer contains a `struct { u4 len; char[len] desc}`
404404
// describing the problem
405+
_encode_oome_in_vm = 4 // an OutOfMemoryError thrown from within VM code on a
406+
// thread that cannot call Java (OOME has no stack trace)
405407
};
406408

407409
JVMCIEnv* _from_env; // Source of translation. Can be null.
@@ -488,6 +490,12 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
488490

489491
int encode(JavaThread* THREAD, jlong buffer, int buffer_size) {
490492
if (!THREAD->can_call_java()) {
493+
Symbol *ex_name = _throwable->klass()->name();
494+
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
495+
JVMCI_event_1("translating exception: OutOfMemoryError within VM code");
496+
decode(THREAD, _encode_oome_in_vm, 0L);
497+
return 0;
498+
}
491499
char* char_buffer = print_throwable_to_buffer(_throwable, buffer, buffer_size);
492500
const char* detail = log_is_enabled(Info, exceptions) ? "" : " (-Xlog:exceptions may give more detail)";
493501
JVMCI_event_1("cannot call Java to translate exception%s: %s", detail, char_buffer);

src/hotspot/share/utilities/exceptions.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,9 @@ bool Exceptions::special_exception(JavaThread* thread, const char* file, int lin
111111
}
112112
#endif // ASSERT
113113

114-
if (!thread->can_call_java()) {
114+
if (h_exception.is_null() && !thread->can_call_java()) {
115115
ResourceMark rm(thread);
116-
const char* exc_value = h_exception.not_null() ? h_exception->print_value_string() :
117-
h_name != nullptr ? h_name->as_C_string() :
118-
"null";
116+
const char* exc_value = h_name != nullptr ? h_name->as_C_string() : "null";
119117
log_info(exceptions)("Thread cannot call Java so instead of throwing exception <%s%s%s> (" PTR_FORMAT ") \n"
120118
"at [%s, line %d]\nfor thread " PTR_FORMAT ",\n"
121119
"throwing pre-allocated exception: %s",
@@ -205,7 +203,7 @@ void Exceptions::_throw_msg_cause(JavaThread* thread, const char* file, int line
205203
void Exceptions::_throw_cause(JavaThread* thread, const char* file, int line, Symbol* name, Handle h_cause,
206204
Handle h_loader, Handle h_protection_domain) {
207205
// Check for special boot-strapping/compiler-thread handling
208-
if (special_exception(thread, file, line, h_cause)) return;
206+
if (special_exception(thread, file, line, Handle(), name)) return;
209207
// Create and throw exception
210208
Handle h_exception = new_exception(thread, name, h_cause, h_loader, h_protection_domain);
211209
_throw(thread, file, line, h_exception, nullptr);

src/java.base/share/classes/jdk/internal/vm/TranslatedException.java

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@
4242

4343
/**
4444
* Support for translating exceptions between the HotSpot heap and libjvmci heap.
45+
*
46+
* Successfully translated exceptions are wrapped in a TranslatedException instance.
47+
* This allows callers to distiguish between a translated exception and an error
48+
* that arose during translation.
4549
*/
4650
@SuppressWarnings("serial")
47-
final class TranslatedException extends Exception {
51+
public final class TranslatedException extends Exception {
4852

4953
/**
5054
* The value returned by {@link #encodeThrowable(Throwable)} when encoding
@@ -61,15 +65,18 @@ final class TranslatedException extends Exception {
6165
maybeFailClinit();
6266
try {
6367
FALLBACK_ENCODED_THROWABLE_BYTES =
64-
encodeThrowable(new TranslatedException("error during encoding",
65-
"<unknown>"), false);
68+
encodeThrowable(translationFailure("error during encoding"), false);
6669
FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES =
67-
encodeThrowable(new OutOfMemoryError(), false);
70+
encodeThrowable(translationFailure("OutOfMemoryError during encoding"), false);
6871
} catch (IOException e) {
6972
throw new InternalError(e);
7073
}
7174
}
7275

76+
private static InternalError translationFailure(String messageFormat, Object... messageArgs) {
77+
return new InternalError(messageFormat.formatted(messageArgs));
78+
}
79+
7380
/**
7481
* Helper to test exception translation.
7582
*/
@@ -86,14 +93,8 @@ private static void maybeFailClinit() {
8693
}
8794
}
8895

89-
/**
90-
* Class name of exception that could not be instantiated.
91-
*/
92-
private String originalExceptionClassName;
93-
94-
private TranslatedException(String message, String originalExceptionClassName) {
95-
super(message);
96-
this.originalExceptionClassName = originalExceptionClassName;
96+
TranslatedException(Throwable translated) {
97+
super(translated);
9798
}
9899

99100
/**
@@ -106,18 +107,6 @@ public Throwable fillInStackTrace() {
106107
return this;
107108
}
108109

109-
@Override
110-
public String toString() {
111-
String s;
112-
if (originalExceptionClassName.equals(TranslatedException.class.getName())) {
113-
s = getClass().getName();
114-
} else {
115-
s = getClass().getName() + "[" + originalExceptionClassName + "]";
116-
}
117-
String message = getMessage();
118-
return (message != null) ? (s + ": " + message) : s;
119-
}
120-
121110
/**
122111
* Prints a stack trace for {@code throwable} if the system property
123112
* {@code "jdk.internal.vm.TranslatedException.debug"} is true.
@@ -163,7 +152,7 @@ private static Throwable create(String className, String message, Throwable caus
163152
return initCause((Throwable) cons.newInstance(message), cause, debug);
164153
} catch (Throwable translationFailure) {
165154
debugPrintStackTrace(translationFailure, debug);
166-
return initCause(new TranslatedException(message, className), cause, debug);
155+
return initCause(translationFailure("%s [%s]", message, className), cause, debug);
167156
}
168157
}
169158

@@ -308,11 +297,10 @@ static Throwable decodeThrowable(byte[] encodedThrowable, boolean debug) {
308297
throwable.setStackTrace(stackTrace);
309298
cause = throwable;
310299
}
311-
return throwable;
300+
return new TranslatedException(throwable);
312301
} catch (Throwable translationFailure) {
313302
debugPrintStackTrace(translationFailure, debug);
314-
return new TranslatedException("Error decoding exception: " + encodedThrowable,
315-
translationFailure.getClass().getName());
303+
return translationFailure("error decoding exception: %s", encodedThrowable);
316304
}
317305
}
318306
}

src/java.base/share/classes/jdk/internal/vm/VMSupport.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,25 @@ public static byte[] serializeAgentPropertiesToByteArray() throws IOException {
122122
* 2: an OutOfMemoryError was thrown while encoding the exception
123123
* 3: some other problem occured while encoding the exception. If {@code buffer != 0},
124124
* it contains a {@code struct { u4 len; char[len] desc}} where {@code desc} describes the problem
125+
* 4: an OutOfMemoryError thrown from within VM code on a
126+
* thread that cannot call Java (OOME has no stack trace)
125127
* </pre>
126128
* @param buffer encoded info about the exception to throw (depends on {@code format})
127129
* @param inJVMHeap [@code true} if executing in the JVM heap, {@code false} otherwise
128130
* @param debug specifies whether debug stack traces should be enabled in case of translation failure
129131
*/
130132
public static void decodeAndThrowThrowable(int format, long buffer, boolean inJVMHeap, boolean debug) throws Throwable {
131133
if (format != 0) {
134+
if (format == 4) {
135+
throw new TranslatedException(new OutOfMemoryError("in VM code and current thread cannot call Java"));
136+
}
132137
String context = String.format("while encoding an exception to translate it %s the JVM heap",
133138
inJVMHeap ? "to" : "from");
134139
if (format == 1) {
135140
throw new InternalError("native buffer could not be allocated " + context);
136141
}
137142
if (format == 2) {
138-
throw new OutOfMemoryError("OutOfMemoryError occurred " + context);
143+
throw new OutOfMemoryError(context);
139144
}
140145
if (format == 3 && buffer != 0L) {
141146
byte[] bytes = bufferToBytes(buffer);

test/jdk/jdk/internal/vm/TestTranslatedException.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.testng.annotations.Test;
4141

4242
import jdk.internal.misc.Unsafe;
43+
import jdk.internal.vm.TranslatedException;
4344
import jdk.internal.vm.VMSupport;
4445

4546
public class TestTranslatedException {
@@ -100,6 +101,15 @@ public void encodeDecodeTest() throws Exception {
100101
try {
101102
VMSupport.decodeAndThrowThrowable(4, 0L, true, false);
102103
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
104+
} catch (TranslatedException decoded) {
105+
Assert.assertEquals(decoded.getCause().getClass(), OutOfMemoryError.class);
106+
} catch (Throwable decoded) {
107+
throw new AssertionError("unexpected exception: " + decoded);
108+
}
109+
110+
try {
111+
VMSupport.decodeAndThrowThrowable(5, 0L, true, false);
112+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
103113
} catch (InternalError decoded) {
104114
// Expected
105115
} catch (Throwable decoded) {
@@ -142,7 +152,7 @@ private void encodeDecode(Throwable throwable) throws Exception {
142152
VMSupport.decodeAndThrowThrowable(format, buffer, true, false);
143153
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
144154
} catch (Throwable decoded) {
145-
assertThrowableEquals(throwable, decoded);
155+
assertThrowableEquals(throwable, decoded.getCause());
146156
}
147157
return;
148158
}
@@ -152,13 +162,15 @@ private void encodeDecode(Throwable throwable) throws Exception {
152162
}
153163
}
154164

155-
private static void assertThrowableEquals(Throwable original, Throwable decoded) {
165+
private static void assertThrowableEquals(Throwable originalIn, Throwable decodedIn) {
166+
Throwable original = originalIn;
167+
Throwable decoded = decodedIn;
156168
try {
157169
Assert.assertEquals(original == null, decoded == null);
158170
while (original != null) {
159171
if (Untranslatable.class.equals(original.getClass())) {
160-
Assert.assertEquals(decoded.getClass().getName(), "jdk.internal.vm.TranslatedException");
161-
Assert.assertEquals(decoded.toString(), "jdk.internal.vm.TranslatedException[jdk.internal.vm.test.TestTranslatedException$Untranslatable]: test exception");
172+
Assert.assertEquals(decoded.getClass().getName(), "java.lang.InternalError");
173+
Assert.assertEquals(decoded.toString(), "java.lang.InternalError: test exception [jdk.internal.vm.test.TestTranslatedException$Untranslatable]");
162174
Assert.assertEquals(original.getMessage(), "test exception");
163175
} else {
164176
Assert.assertEquals(decoded.getClass().getName(), original.getClass().getName());
@@ -182,10 +194,10 @@ private static void assertThrowableEquals(Throwable original, Throwable decoded)
182194
}
183195
} catch (AssertionError e) {
184196
System.err.println("original:[");
185-
original.printStackTrace(System.err);
197+
originalIn.printStackTrace(System.err);
186198
System.err.println("]");
187199
System.err.println("decoded:[");
188-
original.printStackTrace(System.err);
200+
decodedIn.printStackTrace(System.err);
189201
System.err.println("]");
190202
throw e;
191203
}

0 commit comments

Comments
 (0)