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);
+ }
}