Remove StackTraceSimplifier

Related issue: #2735

Motivation:

When an application is under load and experiencing a lot of failure, the
instantiation of DefaultExceptionEvent spends noticeable amount of time
because of StackTraceSimplifier.

Also, StackTraceSimplifier makes people sometimes confused because it
hides the execution path partially.

Modifications:

Remove the use of StackTraceSimplifier

Result:

JVM spends much less time at Throwable.getStackTrace()
This commit is contained in:
Trustin Lee 2014-08-05 18:03:15 -07:00
parent 2b16d9f670
commit 8321035684
5 changed files with 1 additions and 289 deletions

View File

@ -17,8 +17,6 @@ package org.jboss.netty.channel;
import static org.jboss.netty.channel.Channels.*;
import org.jboss.netty.util.internal.StackTraceSimplifier;
/**
* The default {@link ExceptionEvent} implementation.
*/
@ -39,7 +37,6 @@ public class DefaultExceptionEvent implements ExceptionEvent {
}
this.channel = channel;
this.cause = cause;
StackTraceSimplifier.simplify(cause);
}
public Channel getChannel() {

View File

@ -15,8 +15,6 @@
*/
package org.jboss.netty.logging;
import org.jboss.netty.util.internal.StackTraceSimplifier;
/**
* Creates an {@link InternalLogger} or changes the default factory
* implementation. This factory allows you to choose what logging framework
@ -37,13 +35,6 @@ import org.jboss.netty.util.internal.StackTraceSimplifier;
public abstract class InternalLoggerFactory {
private static volatile InternalLoggerFactory defaultFactory = new JdkLoggerFactory();
static {
// Load the dependent classes in advance to avoid the case where
// the VM fails to load the required classes because of too many open
// files.
StackTraceSimplifier.simplify(new Exception());
}
/**
* Returns the default factory. The initial default factory is
* {@link JdkLoggerFactory}.
@ -73,74 +64,7 @@ public abstract class InternalLoggerFactory {
* Creates a new logger instance with the specified name.
*/
public static InternalLogger getInstance(String name) {
final InternalLogger logger = getDefaultFactory().newInstance(name);
return new InternalLogger() {
public void debug(String msg) {
logger.debug(msg);
}
public void debug(String msg, Throwable cause) {
StackTraceSimplifier.simplify(cause);
logger.debug(msg, cause);
}
public void error(String msg) {
logger.error(msg);
}
public void error(String msg, Throwable cause) {
StackTraceSimplifier.simplify(cause);
logger.error(msg, cause);
}
public void info(String msg) {
logger.info(msg);
}
public void info(String msg, Throwable cause) {
StackTraceSimplifier.simplify(cause);
logger.info(msg, cause);
}
public boolean isDebugEnabled() {
return logger.isDebugEnabled();
}
public boolean isErrorEnabled() {
return logger.isErrorEnabled();
}
public boolean isInfoEnabled() {
return logger.isInfoEnabled();
}
public boolean isWarnEnabled() {
return logger.isWarnEnabled();
}
public void warn(String msg) {
logger.warn(msg);
}
public void warn(String msg, Throwable cause) {
StackTraceSimplifier.simplify(cause);
logger.warn(msg, cause);
}
public boolean isEnabled(InternalLogLevel level) {
return logger.isEnabled(level);
}
public void log(InternalLogLevel level, String msg) {
logger.log(level, msg);
}
public void log(InternalLogLevel level, String msg, Throwable cause) {
StackTraceSimplifier.simplify(cause);
logger.log(level, msg, cause);
}
};
return getDefaultFactory().newInstance(name);
}
/**

View File

@ -1,86 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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 org.jboss.netty.util.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.util.DebugUtil;
import org.jboss.netty.util.ThreadRenamingRunnable;
/**
* Simplifies an exception stack trace by removing unnecessary
* {@link StackTraceElement}s. Please note that the stack trace simplification
* is disabled if {@linkplain DebugUtil debug mode} is turned on.
*/
public final class StackTraceSimplifier {
private static final boolean SIMPLIFY_STACK_TRACE = !DebugUtil.isDebugEnabled();
private static final Pattern EXCLUDED_STACK_TRACE =
Pattern.compile(
"^org\\.jboss\\.netty\\." +
"(util\\.(ThreadRenamingRunnable|internal\\.DeadLockProofWorker)" +
"|channel\\.(SimpleChannel(Upstream|Downstream)?Handler|(Default|Static)ChannelPipeline.*))(\\$.*)?$");
/**
* Removes unnecessary {@link StackTraceElement}s from the specified
* exception. {@link ThreadRenamingRunnable}, {@link SimpleChannelHandler},
* and {@link ChannelPipeline} implementations will be dropped from the
* trace.
*/
public static void simplify(Throwable e) {
if (!SIMPLIFY_STACK_TRACE) {
return;
}
if (e.getCause() != null) {
simplify(e.getCause());
}
StackTraceElement[] trace = e.getStackTrace();
if (trace == null || trace.length == 0) {
return;
}
// Perhaps Netty bug. Let us not strip things out.
if (EXCLUDED_STACK_TRACE.matcher(trace[0].getClassName()).matches()) {
return;
}
List<StackTraceElement> simpleTrace =
new ArrayList<StackTraceElement>(trace.length);
simpleTrace.add(trace[0]);
// Remove unnecessary stack trace elements.
for (int i = 1; i < trace.length; i ++) {
if (EXCLUDED_STACK_TRACE.matcher(trace[i].getClassName()).matches()) {
continue;
}
simpleTrace.add(trace[i]);
}
e.setStackTrace(
simpleTrace.toArray(new StackTraceElement[simpleTrace.size()]));
}
private StackTraceSimplifier() {
// Unused
}
}

View File

@ -49,11 +49,6 @@ public class InternalLoggerFactoryTest {
InternalLoggerFactory.setDefaultFactory(null);
}
@Test
public void shouldReturnWrappedLogger() {
assertNotSame(mock, InternalLoggerFactory.getInstance("mock"));
}
@Test
public void testIsDebugEnabled() {
expect(mock.isDebugEnabled()).andReturn(true);

View File

@ -1,118 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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 org.jboss.netty.util.internal;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.DefaultChannelPipeline;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.util.ThreadRenamingRunnable;
import org.junit.Test;
public class StackTraceSimplifierTest {
@Test
public void testBasicSimplification() {
Exception e = new Exception();
e.setStackTrace(new StackTraceElement[] {
new StackTraceElement(ChannelBuffer.class.getName(), "a", null, 1),
new StackTraceElement("com.example.Foo", "b", null, 1),
new StackTraceElement(SimpleChannelHandler.class.getName(), "c", null, 1),
new StackTraceElement(ThreadRenamingRunnable.class.getName(), "d", null, 1),
});
StackTraceSimplifier.simplify(e);
StackTraceElement[] simplified = e.getStackTrace();
assertEquals(2, simplified.length);
assertEquals(ChannelBuffer.class.getName(), simplified[0].getClassName());
assertEquals("com.example.Foo", simplified[1].getClassName());
}
@Test
public void testNestedSimplification() {
Exception e1 = new Exception();
e1.setStackTrace(new StackTraceElement[] {
new StackTraceElement(ChannelBuffer.class.getName(), "a", null, 1),
new StackTraceElement("com.example.Foo", "b", null, 1),
new StackTraceElement(SimpleChannelHandler.class.getName(), "c", null, 1),
new StackTraceElement(DefaultChannelPipeline.class.getName(), "d", null, 1),
new StackTraceElement(ThreadRenamingRunnable.class.getName(), "e", null, 1),
});
Exception e2 = new Exception(e1);
e2.setStackTrace(new StackTraceElement[] {
new StackTraceElement(Channel.class.getName(), "a", null, 1),
new StackTraceElement("com.example.Bar", "b", null, 1),
new StackTraceElement(SimpleChannelHandler.class.getName(), "c", null, 1),
new StackTraceElement(DefaultChannelPipeline.class.getName(), "d", null, 1),
new StackTraceElement(ThreadRenamingRunnable.class.getName(), "e", null, 1),
});
StackTraceSimplifier.simplify(e2);
StackTraceElement[] simplified1 = e1.getStackTrace();
assertEquals(2, simplified1.length);
assertEquals(ChannelBuffer.class.getName(), simplified1[0].getClassName());
assertEquals("com.example.Foo", simplified1[1].getClassName());
StackTraceElement[] simplified2 = e2.getStackTrace();
assertEquals(2, simplified2.length);
assertEquals(Channel.class.getName(), simplified2[0].getClassName());
assertEquals("com.example.Bar", simplified2[1].getClassName());
}
@Test
public void testNettyBugDetection() {
Exception e = new Exception();
e.setStackTrace(new StackTraceElement[] {
new StackTraceElement(DefaultChannelPipeline.class.getName(), "a", null, 1),
new StackTraceElement(ChannelBuffer.class.getName(), "a", null, 1),
new StackTraceElement("com.example.Foo", "b", null, 1),
new StackTraceElement(SimpleChannelHandler.class.getName(), "c", null, 1),
new StackTraceElement(ThreadRenamingRunnable.class.getName(), "d", null, 1),
});
StackTraceSimplifier.simplify(e);
StackTraceElement[] simplified = e.getStackTrace();
assertEquals(5, simplified.length);
}
@Test
public void testEmptyStackTrace() {
Exception e = new Exception();
e.setStackTrace(new StackTraceElement[0]);
StackTraceSimplifier.simplify(e);
assertEquals(0, e.getStackTrace().length);
}
@Test
public void testNullStackTrace() {
Exception e = createNiceMock(Exception.class);
expect(e.getStackTrace()).andReturn(null).anyTimes();
replay(e);
StackTraceSimplifier.simplify(e);
assertNull(e.getStackTrace());
verify(e);
}
}