[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user asfgit closed the pull request at: https://github.com/apache/drill/pull/1070 ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user vrozov commented on a diff in the pull request: https://github.com/apache/drill/pull/1070#discussion_r158335955 --- Diff: exec/memory/base/src/main/java/org/apache/drill/exec/memory/BoundsChecking.java --- @@ -17,19 +17,92 @@ */ package org.apache.drill.exec.memory; +import java.lang.reflect.Field; +import java.util.Formatter; + +import com.google.common.base.Preconditions; + +import io.netty.buffer.AbstractByteBuf; +import io.netty.buffer.DrillBuf; +import io.netty.util.IllegalReferenceCountException; + +import static org.apache.drill.exec.util.SystemPropertyUtil.getBoolean; + public class BoundsChecking { - static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); - public static final boolean BOUNDS_CHECKING_ENABLED; + public static final String ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY = "drill.exec.memory.enable_unsafe_bounds_check"; + // for backward compatibility check "drill.enable_unsafe_memory_access" property and enable bounds checking when + // unsafe memory access is explicitly disabled + public static final String ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY = "drill.enable_unsafe_memory_access"; + public static final boolean BOUNDS_CHECKING_ENABLED = + getBoolean(ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY, !getBoolean(ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY, true)); + private static final boolean checkAccessible = getStaticBooleanField(AbstractByteBuf.class, "checkAccessible", false); static { -boolean isAssertEnabled = false; -assert isAssertEnabled = true; -BOUNDS_CHECKING_ENABLED = isAssertEnabled -|| !"true".equals(System.getProperty("drill.enable_unsafe_memory_access")); +if (BOUNDS_CHECKING_ENABLED) { + logger.warn("Drill is running with direct memory bounds checking enabled. If this is a production system, disable it."); +} else if (logger.isDebugEnabled()) { + logger.debug("Direct memory bounds checking is disabled."); +} } private BoundsChecking() { } + private static boolean getStaticBooleanField(Class cls, String name, boolean def) { +try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(null); +} catch (ReflectiveOperationException e) { + return def; +} + } + + private static void checkIndex(DrillBuf buf, int index, int fieldLength) { +Preconditions.checkNotNull(buf); +if (checkAccessible && buf.refCnt() == 0) { + Formatter formatter = new Formatter().format("%s, refCnt: 0", buf); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IllegalReferenceCountException(formatter.toString()); +} +if (fieldLength < 0) { + throw new IllegalArgumentException(String.format("length: %d (expected: >= 0)", fieldLength)); +} +if (index < 0 || index > buf.capacity() - fieldLength) { + Formatter formatter = new Formatter().format("%s, index: %d, length: %d (expected: range(0, %d))", buf, index, fieldLength, buf.capacity()); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IndexOutOfBoundsException(formatter.toString()); +} + } + + public static void lengthCheck(DrillBuf buf, int start, int length) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, length); +} + } + + public static void rangeCheck(DrillBuf buf, int start, int end) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, end - start); +} + } + + public static void rangeCheck(DrillBuf buf1, int start1, int end1, DrillBuf buf2, int start2, int end2) { --- End diff -- It is a common case for `equal()` and `compare()`. ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user vrozov commented on a diff in the pull request: https://github.com/apache/drill/pull/1070#discussion_r158334992 --- Diff: exec/memory/base/src/main/java/org/apache/drill/exec/memory/BoundsChecking.java --- @@ -17,19 +17,92 @@ */ package org.apache.drill.exec.memory; +import java.lang.reflect.Field; +import java.util.Formatter; + +import com.google.common.base.Preconditions; + +import io.netty.buffer.AbstractByteBuf; +import io.netty.buffer.DrillBuf; +import io.netty.util.IllegalReferenceCountException; + +import static org.apache.drill.exec.util.SystemPropertyUtil.getBoolean; + public class BoundsChecking { - static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); - public static final boolean BOUNDS_CHECKING_ENABLED; + public static final String ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY = "drill.exec.memory.enable_unsafe_bounds_check"; + // for backward compatibility check "drill.enable_unsafe_memory_access" property and enable bounds checking when + // unsafe memory access is explicitly disabled + public static final String ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY = "drill.enable_unsafe_memory_access"; + public static final boolean BOUNDS_CHECKING_ENABLED = + getBoolean(ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY, !getBoolean(ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY, true)); + private static final boolean checkAccessible = getStaticBooleanField(AbstractByteBuf.class, "checkAccessible", false); static { -boolean isAssertEnabled = false; -assert isAssertEnabled = true; -BOUNDS_CHECKING_ENABLED = isAssertEnabled -|| !"true".equals(System.getProperty("drill.enable_unsafe_memory_access")); +if (BOUNDS_CHECKING_ENABLED) { + logger.warn("Drill is running with direct memory bounds checking enabled. If this is a production system, disable it."); +} else if (logger.isDebugEnabled()) { + logger.debug("Direct memory bounds checking is disabled."); +} } private BoundsChecking() { } + private static boolean getStaticBooleanField(Class cls, String name, boolean def) { +try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(null); +} catch (ReflectiveOperationException e) { + return def; +} + } + + private static void checkIndex(DrillBuf buf, int index, int fieldLength) { +Preconditions.checkNotNull(buf); +if (checkAccessible && buf.refCnt() == 0) { + Formatter formatter = new Formatter().format("%s, refCnt: 0", buf); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IllegalReferenceCountException(formatter.toString()); +} +if (fieldLength < 0) { + throw new IllegalArgumentException(String.format("length: %d (expected: >= 0)", fieldLength)); +} +if (index < 0 || index > buf.capacity() - fieldLength) { + Formatter formatter = new Formatter().format("%s, index: %d, length: %d (expected: range(0, %d))", buf, index, fieldLength, buf.capacity()); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IndexOutOfBoundsException(formatter.toString()); +} + } + + public static void lengthCheck(DrillBuf buf, int start, int length) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, length); +} + } + + public static void rangeCheck(DrillBuf buf, int start, int end) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, end - start); +} + } + + public static void rangeCheck(DrillBuf buf1, int start1, int end1, DrillBuf buf2, int start2, int end2) { --- End diff -- AFAIK, there will be no performance difference between a final static pointer and a final static boolean. In both cases, hotspot will optimize it out and inline no-op versions. From code readability point, I would prefer a single class that encapsulates bounds checking functionality. ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user vrozov commented on a diff in the pull request: https://github.com/apache/drill/pull/1070#discussion_r158331061 --- Diff: exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/XXHash.java --- @@ -166,9 +164,7 @@ public static long hash64(double val, long seed){ } public static long hash64(long start, long end, DrillBuf buffer, long seed){ -if (BoundsChecking.BOUNDS_CHECKING_ENABLED) { - buffer.checkBytes((int)start, (int)end); -} +rangeCheck(buffer, (int)start, (int)end); --- End diff -- I don't see `XXHash` class being used anymore and it possibly can be removed as a follow up for DRILL-4237 and DRILL-4478. I'd prefer to limit this PR to bounds checking functionality. ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user paul-rogers commented on a diff in the pull request: https://github.com/apache/drill/pull/1070#discussion_r158164933 --- Diff: exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/XXHash.java --- @@ -166,9 +164,7 @@ public static long hash64(double val, long seed){ } public static long hash64(long start, long end, DrillBuf buffer, long seed){ -if (BoundsChecking.BOUNDS_CHECKING_ENABLED) { - buffer.checkBytes((int)start, (int)end); -} +rangeCheck(buffer, (int)start, (int)end); --- End diff -- Why are the arguments to `hash64` longs? The hash may be 64 bits, but Drill does not support vectors above 2 GB (31 bits) in length, so the `start` and `end` need only be `int`. ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user paul-rogers commented on a diff in the pull request: https://github.com/apache/drill/pull/1070#discussion_r158165417 --- Diff: exec/memory/base/src/main/java/org/apache/drill/exec/memory/BoundsChecking.java --- @@ -17,19 +17,92 @@ */ package org.apache.drill.exec.memory; +import java.lang.reflect.Field; +import java.util.Formatter; + +import com.google.common.base.Preconditions; + +import io.netty.buffer.AbstractByteBuf; +import io.netty.buffer.DrillBuf; +import io.netty.util.IllegalReferenceCountException; + +import static org.apache.drill.exec.util.SystemPropertyUtil.getBoolean; + public class BoundsChecking { - static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); - public static final boolean BOUNDS_CHECKING_ENABLED; + public static final String ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY = "drill.exec.memory.enable_unsafe_bounds_check"; + // for backward compatibility check "drill.enable_unsafe_memory_access" property and enable bounds checking when + // unsafe memory access is explicitly disabled + public static final String ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY = "drill.enable_unsafe_memory_access"; + public static final boolean BOUNDS_CHECKING_ENABLED = + getBoolean(ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY, !getBoolean(ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY, true)); + private static final boolean checkAccessible = getStaticBooleanField(AbstractByteBuf.class, "checkAccessible", false); static { -boolean isAssertEnabled = false; -assert isAssertEnabled = true; -BOUNDS_CHECKING_ENABLED = isAssertEnabled -|| !"true".equals(System.getProperty("drill.enable_unsafe_memory_access")); +if (BOUNDS_CHECKING_ENABLED) { + logger.warn("Drill is running with direct memory bounds checking enabled. If this is a production system, disable it."); +} else if (logger.isDebugEnabled()) { + logger.debug("Direct memory bounds checking is disabled."); +} } private BoundsChecking() { } + private static boolean getStaticBooleanField(Class cls, String name, boolean def) { +try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(null); +} catch (ReflectiveOperationException e) { + return def; +} + } + + private static void checkIndex(DrillBuf buf, int index, int fieldLength) { +Preconditions.checkNotNull(buf); +if (checkAccessible && buf.refCnt() == 0) { + Formatter formatter = new Formatter().format("%s, refCnt: 0", buf); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IllegalReferenceCountException(formatter.toString()); +} +if (fieldLength < 0) { + throw new IllegalArgumentException(String.format("length: %d (expected: >= 0)", fieldLength)); +} +if (index < 0 || index > buf.capacity() - fieldLength) { + Formatter formatter = new Formatter().format("%s, index: %d, length: %d (expected: range(0, %d))", buf, index, fieldLength, buf.capacity()); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IndexOutOfBoundsException(formatter.toString()); +} + } + + public static void lengthCheck(DrillBuf buf, int start, int length) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, length); +} + } + + public static void rangeCheck(DrillBuf buf, int start, int end) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, end - start); +} + } + + public static void rangeCheck(DrillBuf buf1, int start1, int end1, DrillBuf buf2, int start2, int end2) { --- End diff -- This may be easier/faster if we do: * Create a static pointer to a bounds checker. * Two implementations: on and off. * The off implementation simply returns. * The on implementation does the check. * Startup code sets the on or off instance as desired. However, it may be that the compiler generates better code with static functions and final variables... ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
Github user paul-rogers commented on a diff in the pull request: https://github.com/apache/drill/pull/1070#discussion_r158165539 --- Diff: exec/memory/base/src/main/java/org/apache/drill/exec/memory/BoundsChecking.java --- @@ -17,19 +17,92 @@ */ package org.apache.drill.exec.memory; +import java.lang.reflect.Field; +import java.util.Formatter; + +import com.google.common.base.Preconditions; + +import io.netty.buffer.AbstractByteBuf; +import io.netty.buffer.DrillBuf; +import io.netty.util.IllegalReferenceCountException; + +import static org.apache.drill.exec.util.SystemPropertyUtil.getBoolean; + public class BoundsChecking { - static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BoundsChecking.class); - public static final boolean BOUNDS_CHECKING_ENABLED; + public static final String ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY = "drill.exec.memory.enable_unsafe_bounds_check"; + // for backward compatibility check "drill.enable_unsafe_memory_access" property and enable bounds checking when + // unsafe memory access is explicitly disabled + public static final String ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY = "drill.enable_unsafe_memory_access"; + public static final boolean BOUNDS_CHECKING_ENABLED = + getBoolean(ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY, !getBoolean(ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY, true)); + private static final boolean checkAccessible = getStaticBooleanField(AbstractByteBuf.class, "checkAccessible", false); static { -boolean isAssertEnabled = false; -assert isAssertEnabled = true; -BOUNDS_CHECKING_ENABLED = isAssertEnabled -|| !"true".equals(System.getProperty("drill.enable_unsafe_memory_access")); +if (BOUNDS_CHECKING_ENABLED) { + logger.warn("Drill is running with direct memory bounds checking enabled. If this is a production system, disable it."); +} else if (logger.isDebugEnabled()) { + logger.debug("Direct memory bounds checking is disabled."); +} } private BoundsChecking() { } + private static boolean getStaticBooleanField(Class cls, String name, boolean def) { +try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(null); +} catch (ReflectiveOperationException e) { + return def; +} + } + + private static void checkIndex(DrillBuf buf, int index, int fieldLength) { +Preconditions.checkNotNull(buf); +if (checkAccessible && buf.refCnt() == 0) { + Formatter formatter = new Formatter().format("%s, refCnt: 0", buf); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IllegalReferenceCountException(formatter.toString()); +} +if (fieldLength < 0) { + throw new IllegalArgumentException(String.format("length: %d (expected: >= 0)", fieldLength)); +} +if (index < 0 || index > buf.capacity() - fieldLength) { + Formatter formatter = new Formatter().format("%s, index: %d, length: %d (expected: range(0, %d))", buf, index, fieldLength, buf.capacity()); + if (BaseAllocator.DEBUG) { +formatter.format("%n%s", buf.toVerboseString()); + } + throw new IndexOutOfBoundsException(formatter.toString()); +} + } + + public static void lengthCheck(DrillBuf buf, int start, int length) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, length); +} + } + + public static void rangeCheck(DrillBuf buf, int start, int end) { +if (BOUNDS_CHECKING_ENABLED) { + checkIndex(buf, start, end - start); +} + } + + public static void rangeCheck(DrillBuf buf1, int start1, int end1, DrillBuf buf2, int start2, int end2) { --- End diff -- Why do we need a two-buffer version? Why not two calls to the one-buffer version for simplicity? ---
[GitHub] drill pull request #1070: DRILL-6004: Direct buffer bounds checking should b...
GitHub user vrozov opened a pull request: https://github.com/apache/drill/pull/1070 DRILL-6004: Direct buffer bounds checking should be disabled by default You can merge this pull request into a Git repository by running: $ git pull https://github.com/vrozov/drill DRILL-6004 Alternatively you can review and apply these changes as the patch at: https://github.com/apache/drill/pull/1070.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #1070 commit b863f2a0c9c595dacb924747dea6c449c9ee9917 Author: Vlad RozovDate: 2017-12-12T21:37:38Z DRILL-6004: Direct buffer bounds checking should be disabled by default ---