/*
* Copyright (c) 2011, 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.heap;
import static com.sun.max.platform.Platform.*;
import com.sun.max.annotate.*;
import com.sun.max.memory.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.MaxineVM.Phase;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.thread.VmThreadLocal.Nature;
/**
* Log of TLAB allocations. Used for GC debugging at the moment.
*
* The log records three information per TLAB allocation: pc of the allocation site, allocated cell, size of the allocated cell.
* Information is recorded on per-thread log buffers which are flushed when they are full, or at refill / reset time.
* Log is enabled / disabled using a global flag which may be set by other GC debugging tools.
*
* Thread local are allocated directly off virtual memory to avoid complication and disturbance to the heap being debugged.
* The log buffer are allocated on demand.
*/
public final class TLABLog {
public static final String TLAB_LOG_TAIL_THREAD_LOCAL_NAME = "TLAB_LOG_TAIL";
public static boolean TraceTLABAllocation = false;
static {
VMOptions.addFieldOption("-XX:", "TraceTLABAllocation", TLABLog.class, "Trace every allocation from TLABs when in DEBUG mode", Phase.STARTING);
}
/**
* Tail of the thread-local log buffer of a thread. If zero, logging is disabled,or the thread wasn't allocated a log yet.
*/
public static final VmThreadLocal TLAB_LOG_TAIL
= new VmThreadLocal(TLAB_LOG_TAIL_THREAD_LOCAL_NAME, false, "TLABLog: tail of TLAB allocation log, zero if logging disabled", Nature.Single);
static HeapScheme heapScheme;
static Address logBufferAllocator;
public static final int LOG_BUFFER_SIZE = platform().pageSize;
public static final int LOG_BUFFER_HEADER_SIZE = Word.size();
public static final int LOG_RECORD_SIZE = Word.size() * 3;
public static final long LOG_BUFFER_TAIL_MASK = ~((long) LOG_BUFFER_SIZE - 1);
private static void release(Pointer logHead) {
VirtualMemory.deallocate(logHead, Size.fromInt(LOG_BUFFER_SIZE), VirtualMemory.Type.DATA);
}
private static Pointer allocate(Pointer etla) {
Pointer logBuffer = VirtualMemory.allocate(Size.fromInt(LOG_BUFFER_SIZE), VirtualMemory.Type.DATA);
logBuffer.setWord(etla);
int numUnusableWords = ((LOG_BUFFER_SIZE % LOG_RECORD_SIZE) - LOG_BUFFER_HEADER_SIZE) >> Word.widthValue().log2numberOfBytes;
Pointer logBufferEnd = logBuffer.plus(LOG_BUFFER_SIZE);
// Set up the unusable part of the log buffer with self pointers, so that
// one can quickly test if there's enough space for a new record by testing if log.getWord(wordIndex) == log.plusWords(wordIndex)
while (numUnusableWords > 0) {
final Pointer p = logBufferEnd.minusWords(numUnusableWords--);
p.setWord(p);
}
return logBuffer.plus(LOG_BUFFER_HEADER_SIZE);
}
public static void doOnRetireTLAB(Pointer etla) {
Pointer logTail = TLAB_LOG_TAIL.load(etla);
if (!logTail.isZero()) {
// were logging TLAB allocation. Flush them out.
flush(logTail);
if (!TraceTLABAllocation) {
TLAB_LOG_TAIL.store(etla, Pointer.zero());
release(logHead(logTail));
} else {
TLAB_LOG_TAIL.store(etla, logStart(logTail));
}
}
}
public static void doOnRefillTLAB(Pointer etla, Size tlabSize, boolean force) {
if (!(TraceTLABAllocation || force)) {
return;
}
Pointer logTail = TLAB_LOG_TAIL.load(etla);
TLAB_LOG_TAIL.store(etla, logTail.isZero() ? allocate(etla) : logStart(logTail));
}
@INLINE
public static void record(Pointer etla, Pointer allocationSite, Pointer allocatedCell, Size cellSize) {
Pointer logTail = TLAB_LOG_TAIL.load(etla);
if (!logTail.isZero()) {
if (logTail.getWord().asPointer().equals(logTail)) {
flush(logTail);
logTail = logStart(logTail);
}
logTail.setWord(0, allocationSite);
logTail.setWord(1, allocatedCell);
logTail.setWord(2, cellSize);
TLAB_LOG_TAIL.store(etla, logTail.plus(LOG_RECORD_SIZE));
}
}
private static Pointer logHead(Pointer logTail) {
return logTail.and(LOG_BUFFER_TAIL_MASK);
}
private static Pointer logStart(Pointer logTail) {
return logHead(logTail).plus(LOG_BUFFER_HEADER_SIZE);
}
private static Pointer logEnd(Pointer logTail) {
return logTail.and(LOG_BUFFER_TAIL_MASK).plus(LOG_BUFFER_SIZE);
}
private static VmThread logger(Pointer logTail) {
return VmThread.fromTLA(logHead(logTail).getWord().asPointer());
}
public static Pointer flushAndGetStart(Pointer logTail) {
flush(logTail);
return logStart(logTail);
}
@NO_SAFEPOINT_POLLS("GC debugging")
private static void flush(Pointer logTail) {
final boolean lockDisabledSafepoints = Log.lock();
Log.print("TLAB allocation log for ");
Log.printThread(logger(logTail), false);
Pointer p = logStart(logTail);
Log.print(" #records = ");
Log.println(logTail.minus(p).dividedBy(LOG_RECORD_SIZE).toInt());
int logRecordId = 1;
while (p.lessThan(logTail)) {
Log.print(logRecordId++);
Log.print(" ");
Log.print(p.getWord(0).asAddress());
Log.print(", ");
Log.print(p.getWord(1).asAddress());
Log.print(", ");
Log.println(p.getWord(2).asSize().toLong());
p = p.plusWords(3);
}
Log.unlock(lockDisabledSafepoints);
}
}