/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.tele.page; import java.nio.*; import com.sun.max.program.*; import com.sun.max.tele.*; import com.sun.max.tele.TeleVM.TargetLocation.Kind; import com.sun.max.tele.data.*; import com.sun.max.tele.debug.*; import com.sun.max.tele.util.*; import com.sun.max.unsafe.*; /** * A cached page of remote memory contents. * * To avoid double buffering of {@code byte} arrays in the native code that copies bytes from the VM address space, NIO * {@linkplain ByteBuffer#isDirect() direct} {@link ByteBuffer}s are used, unless the target VM is remote. The buffer for each page is * allocated from a global buffer until the global buffer is exhausted. If the target VM is remote or the * global buffer has been exhausted, then the buffer for each page is a heap allocated byte array. */ public class Page extends AbstractVmHolder { private static final int TRACE_VALUE = 1; /** * Access to memory in the {@link TeleVM}. */ private TeleIO teleIO; /** * Generation count of remote memory modification as of the last time this page was refreshed. */ private long epoch = -1; private final long index; /** * The VM epoch the last time we reported a page refresh failure, used to avoid duplicate messages. */ private long lastFailureEpoch = -1; /** * The buffer for this page. */ private final ByteBuffer buffer; private static final long DEFAULT_GLOBAL_DIRECTBUFFER_POOL_SIZE = 100 * 1024 * 1024; public static final long globalDirectBufferPoolSize; static { long size = DEFAULT_GLOBAL_DIRECTBUFFER_POOL_SIZE; final String value = System.getProperty("max.tele.page.directBufferPoolSize"); if (value != null) { try { size = Long.parseLong(value); } catch (NumberFormatException numberFormatException) { TeleWarning.message("Malformed value for the \"max.tele.page.directBuffersPoolSize\" property", numberFormatException); } } globalDirectBufferPoolSize = size; } private static ByteBuffer globalBuffer; /** * Allocates the buffer for a page according to whether or not {@linkplain #useDirectBuffers direct buffers} * are being used. */ private static synchronized ByteBuffer allocate(TeleIO teleIO, ByteOrder byteOrder, long index) { final int pageSize = teleIO.pageSize(); if (useDirectBuffers()) { if (globalBuffer == null) { globalBuffer = ByteBuffer.allocateDirect(1024 * 1024 * 100).order(byteOrder); } if (globalBuffer.remaining() >= pageSize) { final ByteBuffer buffer = globalBuffer.slice().order(byteOrder); globalBuffer.position(globalBuffer.position() + pageSize); buffer.limit(pageSize); return buffer; } } // Not using direct buffers or global buffer is exhausted return ByteBuffer.allocate(pageSize).order(byteOrder); } /** * Decide whether to use direct buffers. * It is counter-productive to use them if the target VM is remote. */ private static boolean useDirectBuffers() { return TeleVM.targetLocation().kind != Kind.REMOTE; } public Page(TeleVM vm, TeleIO teleIO, long index, ByteOrder byteOrder) { super(vm); this.teleIO = teleIO; this.index = index; this.buffer = allocate(teleIO, byteOrder, index); } /** * @return size of the page in bytes. */ public int size() { return teleIO.pageSize(); } /** * @return starting location of the page in remote memory. */ public Address address() { return Address.fromLong(index * size()); } /** * Mark page contents as needful of refreshing, independent of any prior reads. */ public void invalidate() { epoch = -1; } /** * Reads into the cache the contents of the remote memory page. * * @throws DataIOError */ private void refreshRead() throws DataIOError { if (epoch < teleIO.epoch()) { try { DataIO.Static.readFully(teleIO, address(), buffer); epoch = teleIO.epoch(); } catch (DataIOError e) { if (!(e instanceof ConcurrentDataIOError)) { if (lastFailureEpoch != teleIO.epoch()) { Trace.line(TRACE_VALUE, tracePrefix() + "PAGE REFRESH FAILURE @ " + e.faultAddress.to0xHexString()); lastFailureEpoch = teleIO.epoch(); //TeleWarning.message(e); } } throw e; } catch (TerminatedProcessIOException e) { } } } public byte readByte(int offset) throws DataIOError { refreshRead(); return buffer.get(offset); } public short readShort(int offset) { refreshRead(); return buffer.getShort(offset); } public int readInt(int offset) { refreshRead(); final int result = buffer.getInt(offset); return result; } public long readLong(int offset) { refreshRead(); final long result = buffer.getLong(offset); return result; } /** * Transfers {@code n} bytes from this page to a given buffer where * {@code n == min(dst.limit() - dstOffset, size() - offset)}. * * @param offset the offset in this page from which to start reading * @param dst the buffer into which the bytes will be copied * @param dstOffset the offset in {@code dst} at which to start writing * @return the number of bytes copied */ public int readBytes(int offset, ByteBuffer dst, int dstOffset) throws DataIOError { refreshRead(); final int n = Math.min(dst.limit() - dstOffset, size() - offset); final ByteBuffer srcSlice = buffer.duplicate(); final ByteBuffer dstSlice = dst.duplicate(); srcSlice.position(offset).limit(offset + n); dstSlice.position(dstOffset).limit(dstOffset + n); dstSlice.put(srcSlice); return n; } }