| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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.apache.drill.exec.memory; |
| |
| import java.lang.reflect.Field; |
| import java.util.Formatter; |
| |
| import org.apache.drill.exec.util.AssertionUtil; |
| import com.google.common.base.Preconditions; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| 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 { |
| private static final Logger logger = LoggerFactory.getLogger(BoundsChecking.class); |
| |
| public static final String ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY = "drill.exec.memory.enable_unsafe_bounds_check"; |
| public static final String ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY = "drill.enable_unsafe_memory_access"; |
| public static final boolean BOUNDS_CHECKING_ENABLED = boundsCheckEnabled(); |
| private static final boolean checkAccessible = getStaticBooleanField(AbstractByteBuf.class, "checkAccessible", false); |
| |
| /** |
| * Bounds checking is on either if it is explicitly turned on via a |
| * supported option, or if we're running with assertions enabled, typically |
| * in an IDE. |
| */ |
| private static boolean boundsCheckEnabled() { |
| // for backward compatibility check "drill.enable_unsafe_memory_access" property and enable bounds checking when |
| // unsafe memory access is explicitly disabled. |
| // Enable bounds checking if assertions are enabled (as they are in IDE runs.) |
| return AssertionUtil.isAssertionsEnabled() || |
| getBoolean(ENABLE_UNSAFE_BOUNDS_CHECK_PROPERTY, |
| !getBoolean(ENABLE_UNSAFE_MEMORY_ACCESS_PROPERTY, true)); |
| } |
| |
| static { |
| 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) { |
| if (BOUNDS_CHECKING_ENABLED) { |
| checkIndex(buf1, start1, end1 - start1); |
| checkIndex(buf2, start2, end2 - start2); |
| } |
| } |
| |
| public static void ensureWritable(DrillBuf buf, int minWritableBytes) { |
| if (BOUNDS_CHECKING_ENABLED) { |
| buf.ensureWritable(minWritableBytes); |
| } |
| } |
| } |