blob: 4e7410d386ea30298c1f1d899bd8c6dcccf97f99 [file] [log] [blame]
/*
* 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);
}
}
}