/*
* 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;
import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*;
import com.sun.max.annotate.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.MaxineVM.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.log.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.thread.*;
/**
* Subclasses of this implementation uses native buffers and the "records" are stored as a flattened array. The records
* are, however, not real objects, which requires some subterfuge in the case where a client, e.g, {@link VMLogger} wants
* to see a real object. We handle this with a {@link VmThreadLocal} that holds a {@link NativeRecord
* subclass} of {@link com.sun.max.vm.log.VMLog.Record} which records the native record address, and overrides all the methods that access
* the state of the record. Evidently this record must be "used" promptly and not cached, as the value of the underlying
* state will change every time the {@link NativeRecord#address} is set, which will happen at least every time
* {@link VMLog#getRecord} is called.
*/
public abstract class VMLogNative extends VMLog {
/**
* If true, allocate NativeRecord in heap area not subjected to relocation by the GC.
*/
static boolean PinNativeRecord = false;
static {
VMOptions.addFieldOption("-XX:", "PinNativeRecord", VMLogNative.class, "Allocate VMLog's NativeRecord in heap area not subjected to relocation by the GC", Phase.PRISTINE);
}
/**
* Where the per-thread instance of {@link NativeRecord} is stored.
*/
@INTRINSIC(UNSAFE_CAST) private static native NativeRecord asNativeRecord(Object object);
public static class NativeRecord extends VMLog.Record {
public Pointer address;
public final int argsOffset;
protected NativeRecord(int argsOffset) {
this.argsOffset = argsOffset;
}
@Override
public int getHeader() {
return address.getInt();
}
@Override
public void setHeader(int header) {
address.setInt(header);
}
@Override
public Word getArg(int n) {
return address.getWord(argsOffset, n - 1);
}
@Override
public void setArgs(Word arg1) {
address.setWord(argsOffset, 0, arg1);
}
@Override
public void setArgs(Word arg1, Word arg2) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
}
@Override
public void setArgs(Word arg1, Word arg2, Word arg3) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
address.setWord(argsOffset, 2, arg3);
}
@Override
public void setArgs(Word arg1, Word arg2, Word arg3, Word arg4) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
address.setWord(argsOffset, 2, arg3);
address.setWord(argsOffset, 3, arg4);
}
@Override
public void setArgs(Word arg1, Word arg2, Word arg3, Word arg4, Word arg5) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
address.setWord(argsOffset, 2, arg3);
address.setWord(argsOffset, 3, arg4);
address.setWord(argsOffset, 4, arg5);
}
@Override
public void setArgs(Word arg1, Word arg2, Word arg3, Word arg4, Word arg5, Word arg6) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
address.setWord(argsOffset, 2, arg3);
address.setWord(argsOffset, 3, arg4);
address.setWord(argsOffset, 4, arg5);
address.setWord(argsOffset, 5, arg6);
}
@Override
public void setArgs(Word arg1, Word arg2, Word arg3, Word arg4, Word arg5, Word arg6, Word arg7) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
address.setWord(argsOffset, 2, arg3);
address.setWord(argsOffset, 3, arg4);
address.setWord(argsOffset, 4, arg5);
address.setWord(argsOffset, 5, arg6);
address.setWord(argsOffset, 6, arg7);
}
@Override
public void setArgs(Word arg1, Word arg2, Word arg3, Word arg4, Word arg5, Word arg6, Word arg7, Word arg8) {
address.setWord(argsOffset, 0, arg1);
address.setWord(argsOffset, 1, arg2);
address.setWord(argsOffset, 2, arg3);
address.setWord(argsOffset, 3, arg4);
address.setWord(argsOffset, 4, arg5);
address.setWord(argsOffset, 5, arg6);
address.setWord(argsOffset, 6, arg7);
address.setWord(argsOffset, 7, arg8);
}
/**
* Default size of a log record in the native buffer, assuming fixed length, max args.
*/
public final int defaultSize() {
return argsOffset + VMLog.Record.MAX_ARGS * Word.size();
}
}
/**
* Used to communicate with {@link VMLogger} in PRIMORDIAL phase, so allocated at BOOTSTRAPPING.
*/
@CONSTANT_WHEN_NOT_ZERO
protected static NativeRecord primordialNativeRecord;
/**
* Default size of a native log record in bytes.
*/
@CONSTANT_WHEN_NOT_ZERO
@INSPECTED
protected int defaultNativeRecordSize;
/**
* Offset to start of arguments area.
*/
@CONSTANT_WHEN_NOT_ZERO
@INSPECTED
private int nativeRecordArgsOffset;
/**
* Since we must log before it is even possible to call any native functions,
* this byte array provides a pre-allocated, non-moving area.
*/
@CONSTANT_WHEN_NOT_ZERO
private byte[] primordialLogBufferArray;
@CONSTANT_WHEN_NOT_ZERO
private static Offset byteDataOffset;
/**
* Size of primordial logBuffer and subsequently allocated log buffers.
*/
@INSPECTED
protected int logSize;
/**
* Address of the actual data area in {@link #primordialLogBufferArray}.
*/
@INSPECTED
protected Address logBuffer;
/**
* Actual {@link VmThreadLocal} used for {@link VMLogNative.NativeRecord}.
*/
protected VmThreadLocal vmLogNativeRecordTL;
public void setNativeRecordThreadLocal(VmThreadLocal vmLogNativeRecordTL) {
this.vmLogNativeRecordTL = vmLogNativeRecordTL;
}
@Override
public void initialize(MaxineVM.Phase phase) {
super.initialize(phase);
if (phase == MaxineVM.Phase.BOOTSTRAPPING) {
byteDataOffset = VMConfiguration.vmConfig().layoutScheme().byteArrayLayout.getElementOffsetFromOrigin(0);
nativeRecordArgsOffset = getArgsOffset();
primordialNativeRecord = new NativeRecord(nativeRecordArgsOffset);
defaultNativeRecordSize = primordialNativeRecord.defaultSize();
logSize = getLogSize();
primordialLogBufferArray = new byte[logSize];
} else if (phase == MaxineVM.Phase.PRIMORDIAL) {
logBuffer = Reference.fromJava(primordialLogBufferArray).toOrigin().plus(byteDataOffset);
vmLogNativeRecordTL.store3(Reference.fromJava(primordialNativeRecord));
}
}
@NEVER_INLINE
private Reference allocateNativeRecord() {
// We disable logging for this allocation because we cannot log until
// it is set and we could be logging heap allocation
boolean oldState = setThreadState(false);
Reference nativeRecordRef = Reference.zero();
try {
if (PinNativeRecord) {
Heap.enableImmortalMemoryAllocation();
}
nativeRecordRef = Reference.fromJava(new NativeRecord(getArgsOffset()));
} finally {
if (PinNativeRecord) {
Heap.disableImmortalMemoryAllocation();
}
}
setThreadState(oldState);
vmLogNativeRecordTL.store3(nativeRecordRef);
return nativeRecordRef;
}
@INLINE
protected final NativeRecord getNativeRecord(Pointer tla) {
Reference nativeRecordRef = vmLogNativeRecordTL.loadRef(tla);
if (nativeRecordRef.isZero()) {
nativeRecordRef = allocateNativeRecord();
}
NativeRecord record = asNativeRecord(nativeRecordRef.toJava());
return record;
}
/**
* The offset (in bytes) to the start of the arguments in the native record.
*/
protected abstract int getArgsOffset();
/**
* The total size in bytes of a log buffer.
*/
protected abstract int getLogSize();
}