diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index 470543e0bf..26d45785e1 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -33,10 +33,9 @@ public final class ResourceLeakDetector { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ResourceLeakDetector.class); static { - if (ENABLED) { - logger.debug("Resource leak detection enabled."); - } + logger.debug("io.netty.resourceLeakDetection: {}", ENABLED); } + private static final int DEFAULT_SAMPLING_INTERVAL = 113; private static final ResourceLeak NOOP = new ResourceLeak() { diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index e741c5de70..9a95dc3ec2 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -15,10 +15,14 @@ */ package io.netty.util.internal; +import io.netty.util.CharsetUtil; import io.netty.util.internal.chmv8.ConcurrentHashMapV8; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.ServerSocket; @@ -41,6 +45,8 @@ import java.util.regex.Pattern; */ public final class PlatformDependent { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent.class); + private static final boolean IS_ANDROID = isAndroid0(); private static final boolean IS_WINDOWS = isWindows0(); private static final boolean IS_ROOT = isRoot0(); @@ -59,12 +65,15 @@ public final class PlatformDependent { private static final boolean HAS_JAVASSIST = hasJavassist0(); static { - InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent.class); + if (logger.isDebugEnabled()) { + logger.debug("io.netty.preferDirect: {}", DIRECT_BUFFER_PREFERRED); + } + if (!hasUnsafe()) { logger.warn( "Your platform does not provide complete low-level API for accessing direct buffers reliably. " + - "Unless explicitly requested, heap buffer will always be preferred " + - "to avoid potential risk of getting OutOfMemoryError."); + "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + + "unstability."); } } @@ -277,80 +286,154 @@ public final class PlatformDependent { } catch (Exception e) { android = false; } + + logger.debug("Platform: Android"); return android; } private static boolean isWindows0() { - return SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.US).contains("win"); + boolean windows = SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.US).contains("win"); + if (windows) { + logger.debug("Platform: Windows"); + } + return windows; } private static boolean isRoot0() { - Pattern PERMISSION_DENIED = Pattern.compile(".*permission.*denied.*"); - boolean root = false; - if (!isWindows()) { - for (int i = 1023; i > 0; i --) { - ServerSocket ss = null; - try { - ss = new ServerSocket(); - ss.setReuseAddress(true); - ss.bind(new InetSocketAddress(i)); - root = true; - break; - } catch (Exception e) { - // Failed to bind. - // Check the error message so that we don't always need to bind 1023 times. - String message = e.getMessage(); - if (message == null) { - message = ""; - } - message = message.toLowerCase(); - if (PERMISSION_DENIED.matcher(message).matches()) { - break; - } - } finally { - if (ss != null) { - try { - ss.close(); - } catch (Exception e) { - // Ignore. + if (isWindows()) { + return false; + } + + String[] ID_COMMANDS = { "/usr/bin/id", "/bin/id", "id" }; + Pattern UID_PATTERN = Pattern.compile("^(?:0|[1-9][0-9]*)$"); + for (String idCmd: ID_COMMANDS) { + Process p = null; + BufferedReader in = null; + String uid = null; + try { + p = Runtime.getRuntime().exec(new String[] { idCmd, "-u" }); + in = new BufferedReader(new InputStreamReader(p.getInputStream(), CharsetUtil.US_ASCII)); + uid = in.readLine(); + in.close(); + + for (;;) { + try { + int exitCode = p.waitFor(); + if (exitCode != 0) { + uid = null; } + break; + } catch (InterruptedException e) { + // Ignore + } + } + } catch (Exception e) { + uid = null; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // Ignore + } + } + if (p != null) { + p.destroy(); + } + } + + if (uid != null && UID_PATTERN.matcher(uid).matches()) { + logger.debug("UID: {}", uid); + return "0".equals(uid); + } + } + + logger.debug("Could not determine the current UID using /usr/bin/id; attempting to bind at privileged ports."); + + Pattern PERMISSION_DENIED = Pattern.compile(".*(?:denied|not.*permitted).*"); + for (int i = 1023; i > 0; i --) { + ServerSocket ss = null; + try { + ss = new ServerSocket(); + ss.setReuseAddress(true); + ss.bind(new InetSocketAddress(i)); + if (logger.isDebugEnabled()) { + logger.debug("UID: 0 (succeded to bind at port {})", i); + } + return true; + } catch (Exception e) { + // Failed to bind. + // Check the error message so that we don't always need to bind 1023 times. + String message = e.getMessage(); + if (message == null) { + message = ""; + } + message = message.toLowerCase(); + if (PERMISSION_DENIED.matcher(message).matches()) { + break; + } + } finally { + if (ss != null) { + try { + ss.close(); + } catch (Exception e) { + // Ignore. } } } } - return root; + + logger.debug("UID: non-root (failed to bind at any privileged ports)"); + return false; } + @SuppressWarnings("LoopStatementThatDoesntLoop") private static int javaVersion0() { - // Android - if (isAndroid()) { - return 6; + int javaVersion; + + // Not really a loop + for (;;) { + // Android + if (isAndroid()) { + javaVersion = 6; + break; + } + + try { + Class.forName("java.time.Clock", false, Object.class.getClassLoader()); + javaVersion = 8; + break; + } catch (Exception e) { + // Ignore + } + + try { + Class.forName("java.util.concurrent.LinkedTransferQueue", false, BlockingQueue.class.getClassLoader()); + javaVersion = 7; + break; + } catch (Exception e) { + // Ignore + } + + javaVersion = 6; + break; } - try { - Class.forName("java.time.Clock", false, Object.class.getClassLoader()); - return 8; - } catch (Exception e) { - // Ignore + if (logger.isDebugEnabled()) { + logger.debug("Java version: {}", javaVersion); } - - try { - Class.forName("java.util.concurrent.LinkedTransferQueue", false, BlockingQueue.class.getClassLoader()); - return 7; - } catch (Exception e) { - // Ignore - } - - return 6; + return javaVersion; } private static boolean hasUnsafe0() { if (isAndroid()) { + logger.debug("sun.misc.Unsafe: unavailable (Android)"); return false; } boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false); if (noUnsafe) { + logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)"); return false; } @@ -363,11 +446,14 @@ public final class PlatformDependent { } if (!tryUnsafe) { + logger.debug("sun.misc.Unsafe: unavailable (io.netty.tryUnsafe/org.jboss.netty.tryUnsafe)"); return false; } try { - return PlatformDependent0.hasUnsafe(); + boolean hasUnsafe = PlatformDependent0.hasUnsafe(); + logger.debug("sun.misc.Unsafe: {}", hasUnsafe ? "available" : "unavailable"); + return hasUnsafe; } catch (Throwable t) { return false; } @@ -384,13 +470,19 @@ public final class PlatformDependent { private static boolean hasJavassist0() { boolean noJavassist = SystemPropertyUtil.getBoolean("io.netty.noJavassist", false); if (noJavassist) { + logger.debug("Javassist: unavailable (io.netty.noJavassist)"); return false; } try { JavassistTypeParameterMatcherGenerator.generate(Object.class, PlatformDependent.class.getClassLoader()); + logger.debug("Javassist: available"); return true; } catch (Throwable t) { + logger.debug("Javassist: unavailable"); + logger.warn( + "You don't have Javassist in your class path or you don't have enough permission " + + "to load dynamically generated classes. Please check the configuration for better performance."); return false; } } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index 319739e948..e31ed8c5eb 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -15,6 +15,8 @@ */ package io.netty.util.internal; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; import sun.misc.Cleaner; import sun.misc.Unsafe; @@ -28,6 +30,7 @@ import java.nio.ByteBuffer; */ final class PlatformDependent0 { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class); private static final Unsafe UNSAFE; private static final long CLEANER_FIELD_OFFSET; private static final long ADDRESS_FIELD_OFFSET; @@ -50,6 +53,7 @@ final class PlatformDependent0 { } catch (Throwable t) { cleanerField = null; } + logger.debug("java.nio.ByteBuffer.cleaner: {}", cleanerField != null? "available" : "unavailable"); Field addressField; try { @@ -68,6 +72,7 @@ final class PlatformDependent0 { } catch (Throwable t) { addressField = null; } + logger.debug("java.nio.Buffer.address: {}", addressField != null? "available" : "unavailable"); Unsafe unsafe; if (addressField != null && cleanerField != null) { @@ -75,12 +80,21 @@ final class PlatformDependent0 { Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); unsafe = (Unsafe) unsafeField.get(null); + logger.debug("sun.misc.Unsafe.theUnsafe: {}", unsafe != null? "available" : "unavailable"); // Ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK. // https://github.com/netty/netty/issues/1061 // http://www.mail-archive.com/jdk6-dev@openjdk.java.net/msg00698.html - unsafe.getClass().getDeclaredMethod( - "copyMemory", new Class[] { Object.class, long.class, Object.class, long.class, long.class }); + try { + unsafe.getClass().getDeclaredMethod( + "copyMemory", + new Class[] { Object.class, long.class, Object.class, long.class, long.class }); + + logger.debug("sun.misc.Unsafe.copyMemory(Object, long, Object, long, long): available"); + } catch (NoSuchMethodError t) { + logger.debug("sun.misc.Unsafe.copyMemory(Object, long, Object, long, long): unavailable"); + throw t; + } } catch (Throwable cause) { unsafe = null; } @@ -111,7 +125,9 @@ final class PlatformDependent0 { //noinspection DynamicRegexReplaceableByCompiledPattern unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$"); } + UNALIGNED = unaligned; + logger.debug("java.nio.Bits.unaligned: {}", UNALIGNED); } } diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index 7f366d91b3..ad10299b40 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -73,7 +73,7 @@ public final class NioEventLoop extends SingleThreadEventLoop { } } catch (SecurityException e) { if (logger.isDebugEnabled()) { - logger.debug("Unable to get/set System Property '" + key + '\'', e); + logger.debug("Unable to get/set System Property: {}", key, e); } } @@ -85,7 +85,7 @@ public final class NioEventLoop extends SingleThreadEventLoop { SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold; if (logger.isDebugEnabled()) { - logger.debug("Selector auto-rebuild threshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD); + logger.debug("io.netty.selectorAutoRebuildThreshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD); } }