Improve user experience of StackOverflowError (like OOM works currently). Review by: spoon Issue: 4374 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7483 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java b/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java index 375f993..caacaec 100644 --- a/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java +++ b/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
@@ -52,7 +52,11 @@ } catch (OutOfMemoryError e) { logger.log(TreeLogger.ERROR, "OutOfMemoryError: Increase heap size or lower " - + MAX_THREADS_PROPERTY); + + MAX_THREADS_PROPERTY, e); + throw new UnableToCompleteException(); + } catch (StackOverflowError e) { + logger.log(TreeLogger.ERROR, "StackOverflowError: Increase stack size", + e); throw new UnableToCompleteException(); } }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java index 62327ac..b725548 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -943,9 +943,9 @@ } } return new UnableToCompleteException(); - } else if (e instanceof OutOfMemoryError) { - // Rethrow the original exception so the caller can deal with it. - throw (OutOfMemoryError) e; + } else if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } else { logger.log(TreeLogger.ERROR, "Unexpected internal compiler error", e); return new UnableToCompleteException();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java index ba2e24c..12cd083 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -63,9 +63,9 @@ protected static InternalCompilerException translateException(JNode node, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java index 58b0682..34c6efc 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -614,8 +614,8 @@ typeDecls.add(typeDeclaration); return true; - } catch (OutOfMemoryError e) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). + } catch (VirtualMachineError e) { + // Always rethrow VM errors (an attempt to wrap may fail). throw e; } catch (InternalCompilerException ice) { ice.addNode(type); @@ -684,9 +684,9 @@ private InternalCompilerException translateException( AbstractMethodDeclaration amd, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) { @@ -701,9 +701,9 @@ private InternalCompilerException translateException(Statement stmt, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) { @@ -835,9 +835,9 @@ private InternalCompilerException translateException( TypeDeclaration typeDecl, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java index 0b32c72..6b02a02 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -229,9 +229,9 @@ private static class JavaASTGenerationVisitor { private static InternalCompilerException translateException(JNode node, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) { @@ -2611,9 +2611,9 @@ private InternalCompilerException translateException(Object node, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java index b9ecf44..ade9877 100644 --- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java +++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
@@ -451,9 +451,9 @@ private <T extends JsVisitable<T>> InternalCompilerException translateException( T node, Throwable e) { - if (e instanceof OutOfMemoryError) { - // Always rethrow OOMs (might have no memory to load ICE class anyway). - throw (OutOfMemoryError) e; + if (e instanceof VirtualMachineError) { + // Always rethrow VM errors (an attempt to wrap may fail). + throw (VirtualMachineError) e; } InternalCompilerException ice; if (e instanceof InternalCompilerException) {
diff --git a/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java b/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java index e3b38b0..6152dc4 100644 --- a/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java +++ b/dev/core/src/com/google/gwt/dev/util/log/AbstractTreeLogger.java
@@ -45,6 +45,10 @@ static final String OUT_OF_MEMORY_MSG = "Out of memory; to increase the " + "amount of memory, use the -Xmx flag at startup (java -Xmx128M ...)"; + // This message is package-protected so that the unit test can access it. + static final String STACK_OVERFLOW_MSG = "Stack overflow; to increase the " + + "stack size, use the -Xss flag at startup (java -Xss1M ...)"; + public static String getStackTraceAsString(Throwable e) { // Show the exception info for anything other than "UnableToComplete". if (e == null || e instanceof UnableToCompleteException) { @@ -143,14 +147,15 @@ helpInfo); // This logic is intertwined with log(). If a log message is associated - // with an out-of-memory condition, then we turn it into a branch, + // with a special error condition, then we turn it into a branch, // so this method can be called directly from log(). It is of course // also possible for someone to call branch() directly. In either case, we // (1) turn the original message into an ERROR and // (2) drop an extra log message that explains how to recover - if (causedByOutOfMemory(caught)) { + String specialErrorMessage = causedBySpecialError(caught); + if (specialErrorMessage != null) { type = TreeLogger.ERROR; - childLogger.log(type, OUT_OF_MEMORY_MSG, null); + childLogger.log(type, specialErrorMessage, null); } // Decide whether we want to log the branch message eagerly or lazily. @@ -195,9 +200,9 @@ msg = "(Null log message)"; } - // If this log message is caused by being out of memory, we + // If this log message is caused by out of memory or stack overflow, we // provide a little extra help by creating a child log message. - if (causedByOutOfMemory(caught)) { + if (causedBySpecialError(caught) != null) { branch(TreeLogger.ERROR, msg, caught); return; } @@ -310,22 +315,24 @@ TreeLogger.Type type, String msg, Throwable caught, HelpInfo helpInfo); /** - * Scans <code>t</code> and its causes for {@link OutOfMemoryError}. + * Scans <code>t</code> and its causes for {@link OutOfMemoryError} or + * {@link StackOverflowError}. * * @param t a possibly null {@link Throwable} - * @return true if {@link OutOfMemoryError} appears anywhere in the cause list - * or if <code>t</code> is an {@link OutOfMemoryError}. + * @return true if {@link OutOfMemoryError} or {@link StackOverflowError} + * appears anywhere in the cause list or if <code>t</code> is an + * {@link OutOfMemoryError} or {@link StackOverflowError. */ - private boolean causedByOutOfMemory(Throwable t) { - + private String causedBySpecialError(Throwable t) { while (t != null) { if (t instanceof OutOfMemoryError) { - return true; + return OUT_OF_MEMORY_MSG; + } else if (t instanceof StackOverflowError) { + return STACK_OVERFLOW_MSG; } t = t.getCause(); } - - return false; + return null; } private String getLoggerId() {
diff --git a/dev/core/test/com/google/gwt/dev/util/log/AbstractTreeLoggerTest.java b/dev/core/test/com/google/gwt/dev/util/log/AbstractTreeLoggerTest.java index 29bba38..a12d535 100644 --- a/dev/core/test/com/google/gwt/dev/util/log/AbstractTreeLoggerTest.java +++ b/dev/core/test/com/google/gwt/dev/util/log/AbstractTreeLoggerTest.java
@@ -127,4 +127,38 @@ assertTrue(posTstDbgStr < posTstErrStr); assertTrue(posTstErrStr < posOutOfMemory); } + + /** + * We handle stack overflow conditions specially in the logger to provide more + * useful log output. It does some slightly weird stuff like turning a regular + * log() into a branch(), so this test makes sure that doesn't break anything. + */ + public void testStackOverflowLoggerCommitOrderForLog() { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + PrintWriterTreeLogger logger = new PrintWriterTreeLogger(pw); + logger.setMaxDetail(TreeLogger.WARN); + + final String tstDbgStr = "TEST-DEBUG-STRING"; + final String tstErrStr = "TEST-ERROR-STRING"; + + // Emit something that's low-priority and wouldn't show up normally unless + // it had a higher-priority child log event. + TreeLogger branch = logger.branch(TreeLogger.DEBUG, tstDbgStr, null); + assertEquals(-1, sw.toString().indexOf(tstDbgStr)); + + // Emit something that's low-priority but that also has a OOM. + branch.log(TreeLogger.ERROR, tstErrStr, new StackOverflowError()); + + // Make sure both are now there, in the right order. + int posTstDbgStr = sw.toString().indexOf(tstDbgStr); + int posTstErrStr = sw.toString().indexOf(tstErrStr); + int posOutOfMemory = sw.toString().indexOf( + AbstractTreeLogger.STACK_OVERFLOW_MSG); + assertTrue(posTstDbgStr != -1); + assertTrue(posTstErrStr != -1); + assertTrue(posOutOfMemory != -1); + assertTrue(posTstDbgStr < posTstErrStr); + assertTrue(posTstErrStr < posOutOfMemory); + } }