/* * Copyright (c) 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.vm.log.nat.thread; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.memory.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.log.nat.*; import com.sun.max.vm.thread.*; /** * Common superclass for per-thread log buffers. * * Information on the state of the buffer is stored in a {@link VmThreadLocal}, * {@link VMLogNativeThread#vmLogBufferOffsetsTL}. The actual pair of * thread local slots used for this buffer must be set by calling * {@link #setBufferThreadLocals(VmThreadLocal, VmThreadLocal)}. * * The thread local state comprises following: * * <ul> * <li>the offset of the first valid record, {@link #firstOffset}</li> * <li>the offset where the next record should be written, {@link #nextOffset}</li> * <li>a (sticky) bit to indicate that the buffer has wrapped {@link #WRAPPED}. * </ul> * Offsets are in bytes. * <p> * Note that unless the buffer is empty, {@link #firstOffset} is never equal * to {@link #nextOffset}. * * The exact layout of the native buffer, e.g., whether records are fixed size or * variable size is left to the concrete subclass. * * Note that in order for the Inspector to be able to recreate a globally ordered * set of records (by id), we must store the id in the record itself. * */ public abstract class VMLogNativeThread extends VMLogNative { public static final int ID_OFFSET = Ints.SIZE; public static final int ARGS_OFFSET = 2 * Ints.SIZE; public static final int NEXT_OFFSET_MASK = 0x7FFFFFFE; public static final int FIRST_OFFSET_SHIFT = 32; public static final int SHIFTED_FIRST_OFFSET_MASK = 0x7FFFFFE; public static final long WRAPPED = 0x100000000L; // set in FIRST_OFFSET to indicate buffer has wrapped (sticky) public static final long FIRST_OFFSET_WRAP_MASK = 0x7FFFFFF00000000L; /** * We use bit 0 set to 1 to denote that logging is disabled for this thread. * Since this will suppress all calls to {@link #getRecord(int)} we * do not need to worry about masking it out in {@link #getRecord(int)}. */ public static final int DISABLED = 0x1; public static final long DISABLED_MASK = 0x7FFFFFFFFFFFFFFEL; @CONSTANT protected VmThreadLocal vmLogBufferTL; @CONSTANT protected VmThreadLocal vmLogBufferOffsetsTL; @Override protected boolean isPerThread() { return true; } /** * Sets the specific thread locals used to control this log. * @param vmLogBufferTL * @param vmLogBufferOffsetsTL */ public void setBufferThreadLocals(VmThreadLocal vmLogBufferTL, VmThreadLocal vmLogBufferOffsetsTL) { this.vmLogBufferTL = vmLogBufferTL; this.vmLogBufferOffsetsTL = vmLogBufferOffsetsTL; } @Override public void initialize(MaxineVM.Phase phase) { super.initialize(phase); if (phase == MaxineVM.Phase.PRIMORDIAL) { vmLogBufferTL.store3(logBuffer); } } @Override /** * Space for header and the id. */ protected final int getArgsOffset() { return ARGS_OFFSET; } @Override protected int getLogSize() { return logEntries * defaultNativeRecordSize; } @NEVER_INLINE private Pointer allocateBuffer() { Pointer buffer = Memory.allocate(Size.fromInt(logSize)); vmLogBufferTL.store3(buffer); return buffer; } @INLINE protected final Pointer getBuffer(Pointer tla) { Pointer buffer = vmLogBufferTL.load(tla); if (buffer.isZero()) { buffer = allocateBuffer(); } return buffer; } @INLINE protected final int modLogSize(int offset) { // offset is either < logSize or < 2 * logSize return offset < logSize ? offset : offset - logSize; } @Override public boolean setThreadState(boolean value) { int bit = value ? 0 : DISABLED; Pointer tla = VmThread.currentTLA(); Address offsets = vmLogBufferOffsetsTL.load(tla); vmLogBufferOffsetsTL.store3(Address.fromLong((offsets.toLong() & DISABLED_MASK) | bit)); return (offsets.toLong() & DISABLED) == 0; } @Override public boolean threadIsEnabled() { return (vmLogBufferOffsetsTL.load(VmThread.currentTLA()).toLong() & DISABLED) == 0; } // Convenience methods for accessing the data in VMLOG_BUFFER_OFFSETS @INLINE protected final boolean isWrapped(long offsets) { return (offsets & WRAPPED) != 0; } @INLINE protected final int nextOffset(long offsets) { return (int) (offsets & NEXT_OFFSET_MASK); } @INLINE protected final int firstOffset(long offsets) { return (int) ((offsets >> FIRST_OFFSET_SHIFT) & SHIFTED_FIRST_OFFSET_MASK); } }