Skip to content

Commit 145616b

Browse files
committed
[GR-53462] Show the Java stacktrace for internal errors by default
PullRequest: graalpython/3511
2 parents a7d0248 + 801ad72 commit 145616b

File tree

10 files changed

+80
-36
lines changed

10 files changed

+80
-36
lines changed

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/PythonTests.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static org.hamcrest.CoreMatchers.containsString;
2929
import static org.junit.Assert.assertEquals;
3030
import static org.junit.Assert.assertTrue;
31+
import static org.junit.Assert.fail;
3132

3233
import java.io.ByteArrayOutputStream;
3334
import java.io.File;
@@ -38,6 +39,7 @@
3839
import java.io.PrintStream;
3940
import java.util.Collections;
4041
import java.util.Map;
42+
import java.util.function.Consumer;
4143

4244
import org.graalvm.polyglot.Context;
4345
import org.graalvm.polyglot.Engine;
@@ -134,6 +136,13 @@ public static void assertPrints(String expected, Source code) {
134136
assertEquals(expected.replaceAll(" at 0x[0-9a-f]*>", " at 0xabcd>"), result.replaceAll(" at 0x[0-9a-f]*>", " at 0xabcd>"));
135137
}
136138

139+
public static void assertPrintsToStdErr(String expected, String code) {
140+
final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
141+
PythonTests.runScript(new String[0], code, System.out, byteArray);
142+
String result = byteArray.toString().replaceAll("\r\n", "\n");
143+
assertEquals(expected.replaceAll(" at 0x[0-9a-f]*>", " at 0xabcd>"), result.replaceAll(" at 0x[0-9a-f]*>", " at 0xabcd>"));
144+
}
145+
137146
public static Value eval(String code) {
138147
final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
139148
final PrintStream printStream = new PrintStream(byteArray);
@@ -218,16 +227,24 @@ public static Value runScript(Map<String, String> options, String[] args, String
218227
}
219228

220229
public static void runThrowableScript(String[] args, String source, OutputStream out, OutputStream err) {
221-
try {
222-
enterContext(args);
223-
context.eval(createSource(source));
224-
} catch (PolyglotException t) {
230+
runThrowableScript(args, source, out, err, e -> {
225231
try {
226232
Value printExc = context.eval(PRINT_EXC_TO_STDERR);
227-
printExc.execute(t.getGuestObject());
233+
printExc.execute(e.getGuestObject());
228234
} catch (Throwable ex) {
229235
throw new RuntimeException("Error while printing the PolyglotException message to stderr.", ex);
230236
}
237+
});
238+
}
239+
240+
public static void runThrowableScript(String[] args, String source, OutputStream out, OutputStream err,
241+
Consumer<PolyglotException> exceptionHandler) {
242+
try {
243+
enterContext(args);
244+
context.eval(createSource(source));
245+
fail("The eval() should throw");
246+
} catch (PolyglotException t) {
247+
exceptionHandler.accept(t);
231248
} finally {
232249
flush(out, err);
233250
closeContext();

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/builtin/BaseExceptionTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import java.io.IOException;
5151
import java.io.UnsupportedEncodingException;
5252
import java.math.BigInteger;
53+
import java.nio.charset.StandardCharsets;
5354
import java.util.ArrayList;
5455
import java.util.Arrays;
5556
import java.util.HashMap;
@@ -67,6 +68,7 @@
6768
import org.graalvm.polyglot.proxy.ProxyHashMap;
6869
import org.graalvm.polyglot.proxy.ProxyObject;
6970
import org.junit.After;
71+
import org.junit.Assert;
7072
import org.junit.Before;
7173
import org.junit.Ignore;
7274
import org.junit.Test;
@@ -93,6 +95,7 @@ public void setUpTest() {
9395
Builder builder = Context.newBuilder();
9496
builder.allowExperimentalOptions(true);
9597
builder.allowAllAccess(true);
98+
builder.option("engine.WarnInterpreterOnly", "false");
9699
builder.out(out);
97100
builder.err(err);
98101
context = builder.build();
@@ -197,6 +200,24 @@ public void truffleMethodExport() {
197200
assertTrue(main.canExecute());
198201
}
199202

203+
// From https://github.com/oracle/graalpython/issues/298
204+
@Test
205+
public void testHostException() {
206+
try {
207+
context.eval(createSource("import java; java.math.BigInteger.ONE.divide(java.math.BigInteger.ZERO)"));
208+
fail();
209+
} catch (PolyglotException e) {
210+
Assert.assertTrue(e.isHostException());
211+
Assert.assertTrue(e.asHostException() instanceof ArithmeticException);
212+
Assert.assertTrue(e.getMessage(), e.getMessage().contains("divide by zero"));
213+
}
214+
215+
String outString = out.toString(StandardCharsets.UTF_8);
216+
String errString = err.toString(StandardCharsets.UTF_8);
217+
Assert.assertTrue(outString, outString.isEmpty());
218+
Assert.assertTrue(errString, errString.isEmpty());
219+
}
220+
200221
@Test
201222
public void javaArraySet() {
202223
String source = "import java\n" +

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/PosixTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2024, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -25,9 +25,9 @@
2525
*/
2626
package com.oracle.graal.python.test.integration.module;
2727

28-
import static com.oracle.graal.python.test.integration.PythonTests.assertLastLineError;
2928
import static com.oracle.graal.python.test.integration.PythonTests.assertLastLineErrorContains;
3029
import static com.oracle.graal.python.test.integration.PythonTests.assertPrints;
30+
import static com.oracle.graal.python.test.integration.PythonTests.assertPrintsToStdErr;
3131
import static com.oracle.graal.python.test.integration.Utils.IS_WINDOWS;
3232
import static org.junit.Assert.assertEquals;
3333
import static org.junit.Assert.assertTrue;
@@ -177,7 +177,7 @@ public void stdout() {
177177
@Test
178178
public void stderr() {
179179
assumeFalse(IS_WINDOWS);
180-
assertLastLineError("error\n", "import sys; sys.stderr.write('error\\n')");
180+
assertPrintsToStdErr("error\n", "import sys; sys.stderr.write('error\\n')");
181181
}
182182

183183
@Test
@@ -189,7 +189,7 @@ public void printToStdout() {
189189
@Test
190190
public void printToStderr() {
191191
assumeFalse(IS_WINDOWS);
192-
assertLastLineError("1-2...", "import sys; print('1', '2', sep='-', file=sys.stderr, end='...', flush=True)");
192+
assertPrintsToStdErr("1-2...", "import sys; print('1', '2', sep='-', file=sys.stderr, end='...', flush=True)");
193193
}
194194

195195
@Test

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ private static void dump(PythonLanguage language, PythonContext context, Object
136136
}
137137

138138
if (allThreads) {
139-
if (PythonOptions.isWithJavaStacktrace(language)) {
139+
if (PythonOptions.isPExceptionWithJavaStacktrace(language)) {
140140
PrintWriter err = new PrintWriter(context.getStandardErr());
141141
Thread[] ths = context.getThreads();
142142
for (Map.Entry<Thread, StackTraceElement[]> e : Thread.getAllStackTraces().entrySet()) {
@@ -165,7 +165,7 @@ protected void perform(ThreadLocalAction.Access access) {
165165
}
166166
});
167167
} else {
168-
if (PythonOptions.isWithJavaStacktrace(language)) {
168+
if (PythonOptions.isPExceptionWithJavaStacktrace(language)) {
169169
PrintWriter err = new PrintWriter(context.getStandardErr());
170170
err.println();
171171
err.println(Thread.currentThread());

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242

4343
import static com.oracle.graal.python.builtins.modules.io.IONodes.T_WRITE;
4444
import static com.oracle.graal.python.nodes.BuiltinNames.T_SYS;
45-
import static com.oracle.graal.python.runtime.exception.ExceptionUtils.printToStdErr;
4645
import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemExit;
4746
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
4847

@@ -214,20 +213,18 @@ private void handleJavaException(Throwable e) {
214213
if (pe != null) {
215214
throw handlePythonException(pe);
216215
}
217-
try {
218-
boolean exitException = InteropLibrary.getUncached().isException(e) && InteropLibrary.getUncached().getExceptionType(e) == ExceptionType.EXIT;
219-
if (!exitException) {
220-
ExceptionUtils.printPythonLikeStackTrace(getContext(), e);
221-
boolean withJavaStacktrace = PythonOptions.isWithJavaStacktrace(getPythonLanguage());
222-
if (e instanceof AssertionError && !withJavaStacktrace) {
223-
printToStdErr("To get more information about the failed assertion rerun with --python.WithJavaStacktrace=3\n");
224-
}
225-
if (withJavaStacktrace) {
226-
e.printStackTrace();
216+
if (getContext().getOption(PythonOptions.AlwaysRunExcepthook)) {
217+
try {
218+
boolean exitException = InteropLibrary.getUncached().isException(e) && InteropLibrary.getUncached().getExceptionType(e) == ExceptionType.EXIT;
219+
if (!exitException) {
220+
ExceptionUtils.printPythonLikeStackTrace(getContext(), e);
221+
if (PythonOptions.shouldPrintJavaStacktrace(getPythonLanguage(), e)) {
222+
e.printStackTrace();
223+
}
227224
}
225+
} catch (UnsupportedMessageException unsupportedMessageException) {
226+
throw CompilerDirectives.shouldNotReachHere();
228227
}
229-
} catch (UnsupportedMessageException unsupportedMessageException) {
230-
throw CompilerDirectives.shouldNotReachHere();
231228
}
232229
}
233230

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2191,7 +2191,7 @@ private void shutdownThreads() {
21912191
boolean exitException = InteropLibrary.getUncached().isException(e) && InteropLibrary.getUncached().getExceptionType(e) == ExceptionType.EXIT;
21922192
if (!exitException) {
21932193
ExceptionUtils.printPythonLikeStackTrace(e);
2194-
if (PythonOptions.isWithJavaStacktrace(getLanguage())) {
2194+
if (PythonOptions.shouldPrintJavaStacktrace(getLanguage(), e)) {
21952195
e.printStackTrace(new PrintWriter(getStandardErr()));
21962196
}
21972197
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.Map;
4545
import java.util.Optional;
4646

47+
import com.oracle.truffle.api.exception.AbstractTruffleException;
4748
import org.graalvm.options.OptionCategory;
4849
import org.graalvm.options.OptionDescriptor;
4950
import org.graalvm.options.OptionDescriptors;
@@ -230,11 +231,12 @@ private PythonOptions() {
230231
@EngineOption @Option(category = OptionCategory.INTERNAL, help = "Eagerly initialize source sections.", usageSyntax = "true|false") //
231232
public static final OptionKey<Boolean> ForceInitializeSourceSections = new OptionKey<>(false);
232233

233-
@EngineOption @Option(category = OptionCategory.INTERNAL, help = "Print the java stacktrace. Possible modes:" +
234-
" 1 Print Java stacktrace for Java exceptions only." +
234+
@EngineOption @Option(category = OptionCategory.INTERNAL, help = "Print the Java stacktrace for exceptions. Possible modes:" +
235+
" 0 Do not print any Java stacktraces." +
236+
" 1 Print Java stacktrace for Java exceptions only (default)." +
235237
" 2 Print Java stacktrace for Python exceptions only (ATTENTION: this will have a notable performance impact)." +
236-
" 3 Combines 1 and 2.", usageSyntax = "1|2|3") //
237-
public static final OptionKey<Integer> WithJavaStacktrace = new OptionKey<>(0);
238+
" 3 Combines 1 and 2.", usageSyntax = "0|1|2|3") //
239+
public static final OptionKey<Integer> WithJavaStacktrace = new OptionKey<>(1);
238240

239241
@Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "") //
240242
public static final OptionKey<Boolean> CatchGraalPythonExceptionForUnitTesting = new OptionKey<>(false);
@@ -522,12 +524,19 @@ public static int getNodeRecursionLimit() {
522524
return result;
523525
}
524526

525-
public static boolean isWithJavaStacktrace(PythonLanguage language) {
526-
return language.getEngineOption(WithJavaStacktrace) > 0;
527+
public static boolean isPExceptionWithJavaStacktrace(PythonLanguage language) {
528+
return language.getEngineOption(WithJavaStacktrace) >= 2;
527529
}
528530

529-
public static boolean isPExceptionWithJavaStacktrace(PythonLanguage language) {
530-
return language.getEngineOption(WithJavaStacktrace) > 1;
531+
public static boolean shouldPrintJavaStacktrace(PythonLanguage language, Throwable throwable) {
532+
int mode = language.getEngineOption(WithJavaStacktrace);
533+
if (mode == 1) {
534+
return !(throwable instanceof AbstractTruffleException);
535+
} else if (mode == 2) {
536+
return throwable instanceof AbstractTruffleException;
537+
} else {
538+
return mode == 3;
539+
}
531540
}
532541

533542
@TruffleBoundary

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/ErrorMessageFormatter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public static String format(String format, Object... args) {
133133
@TruffleBoundary
134134
private static String getMessage(Throwable exception) {
135135
String message = exception.getClass().getSimpleName() + ": " + exception.getMessage();
136-
if (PythonOptions.isWithJavaStacktrace(PythonLanguage.get(null))) {
136+
if (PythonOptions.shouldPrintJavaStacktrace(PythonLanguage.get(null), exception)) {
137137
StringWriter writer = new StringWriter();
138138
try (PrintWriter pw = new PrintWriter(writer)) {
139139
exception.printStackTrace(pw);

mx.graalpython/suite.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@
453453
"spotbugsIgnoresGenerated": True,
454454
},
455455

456-
# GRAALPYTHON TESTS
456+
# GRAALPYTHON_UNIT_TESTS
457457
"com.oracle.graal.python.test": {
458458
"subDir": "graalpython",
459459
"sourceDirs": ["src"],

0 commit comments

Comments
 (0)