Fixes issue #946 "Warn user to increase max heap size" (that is, when an out of memory error occurs)
Patch by: tobyr, bruce
Review by: bruce
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@978 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 5300345..8a52d2a 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -25,6 +25,10 @@
*/
public abstract class AbstractTreeLogger implements TreeLogger {
+ // This message is package-protected so that the unit test can access it.
+ static final String OUT_OF_MEMORY_MSG = "Out of memory; to increase the "
+ + "amount of memory, use the -Xmx flag at startup (java -Xmx128M ...)";
+
private static class UncommittedBranchData {
public final Throwable caught;
@@ -140,12 +144,23 @@
//
childLogger.parent = this;
- // We can avoid committing this branch entry until and unless a some
+ // We can avoid committing this branch entry until and unless some
// child (or grandchild) tries to log something that is loggable,
// in which case there will be cascading commits of the parent branches.
//
childLogger.uncommitted = new UncommittedBranchData(type, msg, caught);
+ // 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,
+ // 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)) {
+ type = TreeLogger.ERROR;
+ childLogger.log(type, OUT_OF_MEMORY_MSG, null);
+ }
+
// Decide whether we want to log the branch message eagerly or lazily.
//
if (isLoggable(type)) {
@@ -182,6 +197,13 @@
msg = "(Null log message)";
}
+ // If this log message is caused by being out of memory, we
+ // provide a little extra help by creating a child log message.
+ if (causedByOutOfMemory(caught)) {
+ branch(TreeLogger.ERROR, msg, caught);
+ return;
+ }
+
int childIndex = allocateNextChildIndex();
if (isLoggable(type)) {
commitMyBranchEntryInMyParentLogger();
@@ -234,6 +256,25 @@
}
/**
+ * Scans <code>t</code> and its causes for {@link OutOfMemoryError}.
+ *
+ * @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}.
+ */
+ private boolean causedByOutOfMemory(Throwable t) {
+
+ while (t != null) {
+ if (t instanceof OutOfMemoryError) {
+ return true;
+ }
+ t = t.getCause();
+ }
+
+ return false;
+ }
+
+ /**
* Commits the branch after ensuring that the parent logger (if there is one)
* has been committed first.
*/
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
new file mode 100644
index 0000000..8a01351
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/util/log/AbstractTreeLoggerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.log;
+
+import com.google.gwt.core.ext.TreeLogger;
+
+import junit.framework.TestCase;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Tests the <code>AbstractTreeLogger</code>.
+ */
+public class AbstractTreeLoggerTest extends TestCase {
+
+ /**
+ * We handle out-of-memory 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 testOutOfMemoryLoggerCommitOrderForLog() {
+ 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 OutOfMemoryError());
+
+ // 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.OUT_OF_MEMORY_MSG);
+ assertTrue(posTstDbgStr != -1);
+ assertTrue(posTstErrStr != -1);
+ assertTrue(posOutOfMemory != -1);
+ assertTrue(posTstDbgStr < posTstErrStr);
+ assertTrue(posTstErrStr < posOutOfMemory);
+ }
+
+ /**
+ * Low-priority branch points don't actually show low-priority messages unless
+ * they (later) get a child that is loggable.
+ */
+ public void testLazyBranchCommit() {
+ 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 high-priority and will cause both to show up.
+ branch.log(TreeLogger.ERROR, tstErrStr, null);
+
+ // Make sure both are now there, in the right order.
+ int posTstDbgStr = sw.toString().indexOf(tstDbgStr);
+ int posTstErrStr = sw.toString().indexOf(tstErrStr);
+ assertTrue(posTstDbgStr != -1);
+ assertTrue(posTstErrStr != -1);
+ assertTrue(posTstDbgStr < posTstErrStr);
+ }
+
+}