/* * 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.vm.heap; import static com.sun.max.vm.VMConfiguration.*; import static com.sun.max.vm.VMOptions.*; import static com.sun.max.vm.heap.HeapSchemeAdaptor.*; import static com.sun.max.vm.thread.VmThread.*; import static com.sun.max.vm.thread.VmThreadLocal.*; import java.util.*; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.code.*; import com.sun.max.vm.heap.HeapScheme.PIN_SUPPORT_FLAG; import com.sun.max.vm.heap.debug.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.layout.*; import com.sun.max.vm.log.*; import com.sun.max.vm.log.VMLog.Record; import com.sun.max.vm.log.hosted.*; import com.sun.max.vm.monitor.*; import com.sun.max.vm.monitor.modal.sync.*; import com.sun.max.vm.object.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.thread.*; /** * The dynamic Java object heap. */ public final class Heap { @HOSTED_ONLY public static boolean useOutOfLineStubs; // TODO: clean up. Just for indicating that boot image should be generated with inline TLAB allocation if heap scheme supports TLAB. @HOSTED_ONLY public static boolean genInlinedTLAB; @HOSTED_ONLY private static Map<Object, Address> objectToCell; @HOSTED_ONLY public static Address objectToCell(Object object) { return objectToCell.get(object); } @HOSTED_ONLY public static void initObjectToCell(Map<Object, Address> map) { objectToCell = map; } private Heap() { } /** * This field is set to a non-zero value by the native code iff the * heap scheme returns a non-zero value for {@linkplain HeapScheme#reservedVirtualSpaceKB()}. */ private static Address reservedVirtualSpace = Address.zero(); private static final Size MIN_HEAP_SIZE = Size.M.times(4); // To be adjusted /** * If initial size not specified, then it is maxSize / DEFAULT_INIT_HEAP_SIZE_RATIO. */ private static final int DEFAULT_INIT_HEAP_SIZE_RATIO = 2; public static final VMSizeOption maxHeapSizeOption = register(new VMSizeOption("-Xmx", Size.G, "The maximum heap size."), MaxineVM.Phase.PRISTINE); public static final VMSizeOption initialHeapSizeOption = register(new InitialHeapSizeOption(), MaxineVM.Phase.PRISTINE); /** * Avoid using commit / uncommit operations that relies on anonymous memory operations. * These have been shown to be very expensive (on Solaris) although the cause isn't clear yet. * We keep this until we understand better the performance issues. */ public static boolean AvoidsAnonOperations = true; public static boolean OptimizeJNICritical = true; static { VMOptions.addFieldOption("-XX:", "OptimizeJNICritical", Heap.class, "Use GC disabling to optimize JNI 'critical' functions when heap scheme doesn't support object pinning.", MaxineVM.Phase.PRISTINE); VMOptions.addFieldOption("-XX:", "AvoidsAnonOperations", Heap.class, "Avoids using Anonymous Memory operations as much as possible.", MaxineVM.Phase.PRISTINE); } static class InitialHeapSizeOption extends VMSizeOption { String invalidHeapSizeReason; @HOSTED_ONLY public InitialHeapSizeOption() { super("-Xms", maxHeapSizeOption.getValue().dividedBy(DEFAULT_INIT_HEAP_SIZE_RATIO), "The initial heap size."); } @Override public boolean check() { invalidHeapSizeReason = validateHeapSizing(); return invalidHeapSizeReason == null; } @Override public void printErrorMessage() { Log.print(invalidHeapSizeReason); } } /** * Conveys the initial/max size for the heap. The {@code getXXXSize} methods can be overridden by an environment that has more knowledge. * */ public static class HeapSizeInfo { protected Size getInitialSize() { return initialHeapSizeOption.getValue(); } protected Size getMaxSize() { return maxHeapSizeOption.getValue(); } } private static HeapSizeInfo heapSizeInfo = new HeapSizeInfo(); @HOSTED_ONLY public static void registerHeapSizeInfo(HeapSizeInfo theHeapSizeInfo) { heapSizeInfo = theHeapSizeInfo; } /** * Lock for synchronizing access to the heap. */ public static final Object HEAP_LOCK = JavaMonitorManager.newVmLock("HEAP_LOCK"); public static boolean holdsHeapLock() { return Monitor.threadHoldsMonitor(HEAP_LOCK, VmThread.current()); } private static Size maxSize; private static Size initialSize; private static boolean heapSizingInputValidated = false; /** * Start of the virtual space requested by the heap scheme and reserved at boot-load time. * The reserved space isn't backed yet by swap space (i.e., the memory is uncommitted). * The start or end of the reserved virtual space may comprise the boot image. * * @return Address of reserved virtual space. */ public static Address startOfReservedVirtualSpace() { return reservedVirtualSpace; } /** * Validate heap sizing inputs. This is common to any GC and can be done early on. * */ private static String validateHeapSizing() { if (heapSizingInputValidated) { return null; } Size max = heapSizeInfo.getMaxSize(); Size init = heapSizeInfo.getInitialSize(); if (maxHeapSizeOption.isPresent()) { if (max.lessThan(MIN_HEAP_SIZE)) { return "Heap too small"; } if (initialHeapSizeOption.isPresent()) { if (max.lessThan(init)) { return "Incompatible minimum and maximum heap sizes specified"; } if (init.lessThan(MIN_HEAP_SIZE)) { return "Too small initial heap"; } } else { init = max; } } else if (initialHeapSizeOption.isPresent()) { if (init.lessThan(MIN_HEAP_SIZE)) { return "Heap too small"; } max = init; } maxSize = max; initialSize = init; heapSizingInputValidated = true; return null; } // Note: Called via reflection from jvm.c public static long maxSizeLong() { return maxSize().toLong(); } public static Size maxSize() { if (maxSize.isZero()) { validateHeapSizing(); } return maxSize; } public static Size initialSize() { if (initialSize.isZero()) { validateHeapSizing(); } return initialSize; } /** * Determines if information should be displayed about each garbage collection event. * Beyond the {@link VMOptions#verboseOption}, this must depend on tracing being enabled, not logging, * as it will generate output. */ public static boolean verbose() { return verboseOption.verboseGC || gcAllLogger.traceEnabled() || rootScanLogger.traceEnabled() || (timeLogger.traceEnabled() && LogGCSuppressionCount <= 0); } /** * Set the verboseGC option (java.lang.management support). */ public static void setVerbose(boolean value) { verboseOption.verboseGC = value; } private static boolean GCDisabled; static { VMOptions.addFieldOption("-XX:", "DisableGC", Classes.getDeclaredField(Heap.class, "GCDisabled"), "Disable garbage collection.", MaxineVM.Phase.STARTING); } /** * Returns whether the "-XX:+DisableGC" option was specified. * * @return {@code true} if the user specified "-XX:+DisableGC" on the command line option; {@code false} otherwise */ public static boolean gcDisabled() { return GCDisabled; } /** * Used by the Inspector to uniquely identify the special boot heap region. */ @INSPECTED private static final String HEAP_BOOT_NAME = "Heap-Boot"; @INSPECTED public static final BootHeapRegion bootHeapRegion = new BootHeapRegion(Address.zero(), Size.fromInt(Integer.MAX_VALUE), HEAP_BOOT_NAME); @FOLD private static HeapScheme heapScheme() { return vmConfig().heapScheme(); } @INLINE public static void disableAllocationForCurrentThread() { heapScheme().disableAllocationForCurrentThread(); } @INLINE public static void enableAllocationForCurrentThread() { heapScheme().enableAllocationForCurrentThread(); } @INLINE public static boolean isAllocationDisabledForCurrentThread() { return heapScheme().isAllocationDisabledForCurrentThread(); } /** * @see HeapScheme#isGcThread(Thread) */ public static boolean isGcThread(Thread thread) { return heapScheme().isGcThread(thread); } @INLINE public static Object createArray(DynamicHub hub, int length) { final Object array = heapScheme().createArray(hub, length); if (Heap.logAllocation()) { allocationLogger.logCreateArray(hub, length, array); } return array; } @INLINE public static Object createTuple(Hub hub) { final Object object = heapScheme().createTuple(hub); if (Heap.logAllocation()) { allocationLogger.logCreateTuple(hub, object); } return object; } @INLINE public static Object createHybrid(DynamicHub hub) { final Object hybrid = heapScheme().createHybrid(hub); if (Heap.logAllocation()) { allocationLogger.logCreateHybrid(hub, hybrid); } return hybrid; } @INLINE public static Hybrid expandHybrid(Hybrid hybrid, int length) { final Hybrid expandedHybrid = heapScheme().expandHybrid(hybrid, length); if (Heap.logAllocation()) { allocationLogger.logExpandHybrid(ObjectAccess.readHub(hybrid), expandedHybrid); } return expandedHybrid; } @INLINE public static Object clone(Object object) { final Object clone = heapScheme().clone(object); if (Heap.logAllocation()) { allocationLogger.logClone(ObjectAccess.readHub(object), clone); } return clone; } /** * A VM option for triggering a GC at fixed intervals. */ public static int ExcessiveGCFrequency; static { VMOptions.addFieldOption("-XX:", "ExcessiveGCFrequency", Heap.class, "Run a garbage collection every <n> milliseconds. A value of 0 disables this mechanism."); } public static boolean collectGarbage() { if (Heap.gcDisabled()) { Throw.stackDump("Out of memory and GC is disabled"); MaxineVM.native_exit(1); } if (VmThread.isAttaching()) { Log.println("Cannot run GC on a thread still attaching to the VM"); MaxineVM.native_exit(1); } if (VmThread.current().isVmOperationThread()) { // Even if another thread holds the heap lock for the purpose of executing a GC, // the GC is not actually executing as this is the VM operation thread which is // executing another VM operation that triggers a GC. So, the GC is now executed // as a nested VM operation without acquiring the heap lock. VmOperationThread.instance().promoteToGlobalSafepoint(); // We're at a global safepoint, no one can concurrently update disableGCThreadCount. // It is possible that a VM operation (that isn't a GC operation) was running while mutator threads // disabled the GC. If that is the case, the current GC operation is illegal. FatalError.check(disableGCThreadCount == 0, "GC must be enabled"); return heapLockedCollectGarbage(); } else { // Calls to collect garbage need to synchronize on the heap lock. This ensures that // GC operations are submitted serially to the VM operation thread. It also means // that a collection only actually occurs if needed (i.e. concurrent call to this // method by another thread did not trigger a GC that freed up enough memory for // this request). synchronized (HEAP_LOCK) { waitForGCDisablingThreads(); return heapLockedCollectGarbage(); } } } private static boolean heapLockedCollectGarbage() { if (verbose()) { VmThread.current().gcRequest.printBeforeGC(); } final boolean result = heapScheme().collectGarbage(); if (verbose()) { VmThread.current().gcRequest.printAfterGC(result); } return result; } // Note: Called via reflection from jvm.c public static long reportFreeSpace() { return heapScheme().reportFreeSpace().toLong(); } // Note: Called via reflection from jvm.c public static long reportUsedSpace() { return heapScheme().reportUsedSpace().toLong(); } // Note: Called via reflection from jvm.c public static long maxObjectInspectionAge() { return heapScheme().maxObjectInspectionAge(); } public static void lock() { Monitor.enter(HEAP_LOCK); } public static void unlock() { Monitor.exit(HEAP_LOCK); } /** * Determines if the heap scheme is initialized to the point where * {@link #collectGarbage(Size)} can safely be called. */ public static boolean isInitialized() { return VmOperationThread.instance() != null; } public static void enableImmortalMemoryAllocation() { heapScheme().enableCustomAllocation(Word.allOnes().asAddress()); if (ImmortalHeap.immortalHeapLogger.enabled()) { ImmortalHeap.immortalHeapLogger.logEnable(); } } public static void disableImmortalMemoryAllocation() { heapScheme().disableCustomAllocation(); if (ImmortalHeap.immortalHeapLogger.enabled()) { ImmortalHeap.immortalHeapLogger.logDisable(); } } /** * Counter of threads that are disabling GC. * The counter is increased / decreased only when the thread local count changes from zero to one (and vice-versa). * * @see Heap#disableGC() * @see Heap#enableGC() */ private static int disableGCThreadCount = 0; /** * Flag indicating that the GC is waiting for GC-disabling threads. * This is currently used to implement an optimistic form of object pinning for heap schemes that don't * support it, wherein threads disable GC while holding direct pointers to arrays. * * @see Heap#disableGC() * @see Heap#enableGC() */ private static boolean gcWaitForDisablingThreads = false; /** * Disable GC. Must be paired with a subsequent call to {@link Heap#enableGC()} */ @INLINE private static void disableGC() { final Pointer etla = ETLA.load(currentTLA()); Pointer count = GC_DISABLING_COUNT.load(etla); if (count.isZero()) { synchronized (HEAP_LOCK) { disableGCThreadCount++; } } GC_DISABLING_COUNT.store(etla, count.plus(1)); } /** * Enable GC. Must be paired with a previous call to {@link Heap#disableGC()} */ @INLINE private static void enableGC() { final Pointer etla = ETLA.load(currentTLA()); Pointer count = GC_DISABLING_COUNT.load(etla); assert count.greaterThan(Pointer.zero()) : "thread has not issued a GC disabling request"; if (count.equals(1)) { synchronized (HEAP_LOCK) { assert disableGCThreadCount > 0 : "some thread have not issued a GC disabling request"; disableGCThreadCount--; if (disableGCThreadCount == 0 && gcWaitForDisablingThreads) { // Wake up GC if waiting on the HEAP lock. HEAP_LOCK.notifyAll(); } } } GC_DISABLING_COUNT.store(etla, count.minus(1)); } private static void waitForGCDisablingThreads() { while (disableGCThreadCount > 0) { gcWaitForDisablingThreads = true; final Pointer etla = ETLA.load(currentTLA()); FatalError.check(GC_DISABLING_COUNT.load(etla).equals(0), "GC requester must not pin any objects"); try { HEAP_LOCK.wait(); } catch (InterruptedException e) { } } gcWaitForDisablingThreads = false; } @INLINE public static boolean useDirectPointer(Object object) { HeapScheme heapScheme = heapScheme(); if (heapScheme.supportsPinning(PIN_SUPPORT_FLAG.CAN_NEST)) { heapScheme.pin(object); return true; } if (OptimizeJNICritical) { disableGC(); return true; } return false; } @INLINE public static boolean releasedDirectPointer(Object object) { HeapScheme heapScheme = VMConfiguration.vmConfig().heapScheme(); if (heapScheme.supportsPinning(PIN_SUPPORT_FLAG.CAN_NEST)) { heapScheme.unpin(object); return true; } if (OptimizeJNICritical) { enableGC(); return true; } return false; } /** * Determines if a given object is in the boot image. * * @param object and object to check * @return true if {@code object} is in the boot image */ public static boolean isInBootImage(Object object) { Pointer origin = Reference.fromJava(object).toOrigin(); return bootHeapRegion.contains(origin) || Code.contains(origin) || ImmortalHeap.contains(origin); } public static boolean isValidRef(Reference ref) { if (ref.isZero()) { return true; } if (CodePointer.isCodePointer(ref)) { return true; } Pointer origin = ref.toOrigin(); if (!bootHeapRegion.contains(origin) && !heapScheme().contains(origin) && !Code.contains(origin) && !ImmortalHeap.contains(origin)) { return false; } if (DebugHeap.isTagging()) { return DebugHeap.isValidNonnullRef(ref); } return true; } public static void checkHeapSizeOptions() { Size initSize = initialSize(); Size maxSize = maxSize(); if (initSize.greaterThan(maxSize)) { Log.println("Incompatible minimum and maximum heap sizes specified"); MaxineVM.native_exit(1); } } /* * Support for callbacks on GC start and end */ /** * Identifies the callback phase, and can also be used within * heap implementations for logging purposes. */ public static enum GCCallbackPhase { INIT("GC initialization"), BEFORE("before GC"), AFTER("after GC"); public final String description; public static final GCCallbackPhase[] VALUES = values(); @HOSTED_ONLY public static String inspectedValue(Word argValue) { return values()[argValue.asAddress().toInt()].description; } GCCallbackPhase(String traceString) { this.description = traceString; } } /** * A class that wants to register for callbacks must implement this interface * and register itself with {@link Heap#registerGCCallback(GCCallback)}. */ public interface GCCallback { void gcCallback(GCCallbackPhase gcCallbackPhase); } @HOSTED_ONLY private static ArrayList<GCCallback> gcCallbackList = new ArrayList<GCCallback>(); @HOSTED_ONLY public static void registerGCCallback(GCCallback callback) { FatalError.check(gcCallbacks == null, "too late to register a GC callback"); gcCallbackList.add(callback); } @HOSTED_ONLY private static class InitializationCompleteCallback implements JavaPrototype.InitializationCompleteCallback { @Override public void initializationComplete() { gcCallbacks = new GCCallback[gcCallbackList.size()]; gcCallbackList.toArray(gcCallbacks); } } static { JavaPrototype.registerInitializationCompleteCallback(new InitializationCompleteCallback()); } @CONSTANT private static GCCallback[] gcCallbacks; public static void invokeGCCallbacks(GCCallbackPhase callbackPhase) { for (int i = 0; i < gcCallbacks.length; i++) { gcCallbacks[i].gcCallback(callbackPhase); } } /* * Everything related to logging/tracing the heap behavior is defined below. */ /** * A logger for the phases of the GC - implementation provided by the heap scheme. */ public static final PhaseLogger phaseLogger = heapScheme().phaseLogger(); /** * A logger for timing the phases of the GC - implementation provided by the heap scheme. */ public static final TimeLogger timeLogger = heapScheme().timeLogger(); /** * Logger for root scanning. */ public static final RootScanLogger rootScanLogger = new RootScanLogger(); /** * A logger for object allocation, only visible in a DEBUG image build. */ public static final AllocationLogger allocationLogger = MaxineVM.isDebug() ? new AllocationLogger(true) : new AllocationLogger(); /** * A pseudo-logger that exists solely to define the {@code LogGC and TraceGC} options, * which, if enabled, force all the dependent loggers to enabled. It has no operations. */ public static final VMLogger gcAllLogger = new VMLogger("GC", 0, "all garbage collection activity. Enabling this option also enables the " + rootScanLogger.logOption + ", " + phaseLogger.traceOption + " and " + timeLogger.traceOption + " options.", null) { @Override public void checkOptions() { super.checkOptions(); forceDependentLoggerState(phaseLogger, rootScanLogger, timeLogger); } }; static { VMOptions.addFieldOption("-XX:", "LogGCSuppressionCount", Heap.class, "Disable " + gcAllLogger.logOption + ", " + rootScanLogger.logOption + " and " + phaseLogger.logOption + " until the n'th GC"); } /* * Functions that act as guards for logging, and add additional conjunctive constraints * beyond the setting of the log options. */ /** * Determines if object allocation should be logged. * * @return {@code false} if the VM build level is not {@link BuildLevel#DEBUG}. */ @INLINE public static boolean logAllocation() { return MaxineVM.isDebug() && allocationLogger.enabled(); } /** * Determines if all garbage collection activity should be logged. */ @INLINE public static boolean logAllGC() { return gcAllLogger.enabled() && LogGCSuppressionCount <= 0; } /** * Determines if the garbage collection phases should be logged. */ @INLINE public static boolean logGCPhases() { return (gcAllLogger.enabled() || phaseLogger.enabled()) && LogGCSuppressionCount <= 0; } /** * Determines if garbage collection root scanning should be logged. */ @INLINE public static boolean logRootScanning() { return (gcAllLogger.enabled() || rootScanLogger.enabled()) && LogGCSuppressionCount <= 0; } /** * Determines if garbage collection timings should be collected and logged. */ @INLINE public static boolean logGCTime() { return (gcAllLogger.enabled() || timeLogger.enabled()) && LogGCSuppressionCount <= 0; } /** * Disables phase, time and roots logging if greater than 0. */ public static int LogGCSuppressionCount; /* * Logging of root scanning. */ @HOSTED_ONLY @VMLoggerInterface private interface RootScanLoggerInterface { void scanningBootHeap( @VMLogParam(name = "start") Address start, @VMLogParam(name = "end") Address end, @VMLogParam(name = "mutableReferencesEnd") Address mutableReferencesEnd); void visitReferenceMapSlot( @VMLogParam(name = "regionWordIndex") int regionWordIndex, @VMLogParam(name = "address") Pointer address, @VMLogParam(name = "value") Word value); } public static class RootScanLogger extends RootScanLoggerAuto { RootScanLogger() { super("RootScanning", "garbage collection root scanning."); } public void logScanningBootHeap(BootHeapRegion region, Address mutableReferencesEnd) { logScanningBootHeap(region.start(), region.end(), mutableReferencesEnd); } @Override protected void traceScanningBootHeap(Address start, Address end, Address mutableReferencesEnd) { Log.print("Scanning boot heap: start="); Log.print(start); Log.print(", end="); Log.print(end); Log.print(", mutable references end="); Log.println(mutableReferencesEnd); } @Override protected void traceVisitReferenceMapSlot(int regionWordIndex, Pointer address, Word value) { Log.print(" Slot: "); Log.print("index="); Log.print(regionWordIndex); Log.print(", address="); Log.print(address); Log.print(", value="); Log.println(value); } } /* * Logging of object allocation. */ /** * Allocation logging interface. * These methods are very similar in form, but we choose to distinguish them * by the operation (method) name. Most of the implementation will be shared. */ @HOSTED_ONLY @VMLoggerInterface(defaultConstructor = true) private interface AllocationLoggerInterface { void clone( @VMLogParam(name = "classActor") ClassActor classActor, @VMLogParam(name = "cell") Pointer cell, @VMLogParam(name = "size") Size size); void createArray( @VMLogParam(name = "classActor") ClassActor classActor, @VMLogParam(name = "length") int length, @VMLogParam(name = "cell") Pointer cell, @VMLogParam(name = "size") Size size); void createTuple( @VMLogParam(name = "classActor") ClassActor classActor, @VMLogParam(name = "cell") Pointer cell, @VMLogParam(name = "size") Size size); void createHybrid( @VMLogParam(name = "classActor") ClassActor classActor, @VMLogParam(name = "cell") Pointer cell, @VMLogParam(name = "size") Size size); void expandHybrid( @VMLogParam(name = "classActor") ClassActor classActor, @VMLogParam(name = "cell") Pointer cell, @VMLogParam(name = "size") Size size); } /** * Implementation of allocation logging. * The actual methods traffic in {@link Hub} types, which we convert to * {@link ClassActor}, since the tracing simply prints the class name, * and we can log a {@link ClassActor} by its id. */ public final static class AllocationLogger extends AllocationLoggerAuto { AllocationLogger(boolean active) { super("Allocation", "heap allocation."); } AllocationLogger() { super(); } @NEVER_INLINE void logClone(Hub hub, Object clone) { logClone(hub.classActor, Layout.originToCell(ObjectAccess.toOrigin(clone)), hub.tupleSize); } @NEVER_INLINE void logCreateArray(Hub hub, int length, Object array) { logCreateArray(hub.classActor, length, Layout.originToCell(ObjectAccess.toOrigin(array)), Layout.size(Reference.fromJava(array))); } @NEVER_INLINE void logCreateTuple(Hub hub, Object object) { logCreateTuple(hub.classActor, Layout.originToCell(ObjectAccess.toOrigin(object)), hub.tupleSize); } @NEVER_INLINE void logCreateHybrid(Hub hub, Object hybrid) { logCreateHybrid(hub.classActor, Layout.originToCell(ObjectAccess.toOrigin(hybrid)), hub.tupleSize); } @NEVER_INLINE void logExpandHybrid(Hub hub, Hybrid expandedHybrid) { logExpandHybrid(hub.classActor, Layout.originToCell(ObjectAccess.toOrigin(expandedHybrid)), hub.tupleSize); } @Override protected void traceClone(ClassActor classActor, Pointer cell, Size size) { traceAllocation(classActor, cell, size, -1, "clone"); } @Override protected void traceCreateTuple(ClassActor classActor, Pointer cell, Size size) { traceAllocation(classActor, cell, size, -1, "tuple"); } @Override protected void traceCreateHybrid(ClassActor classActor, Pointer cell, Size size) { traceAllocation(classActor, cell, size, -1, "hybrid"); } @Override protected void traceExpandHybrid(ClassActor classActor, Pointer cell, Size size) { traceAllocation(classActor, cell, size, -1, "expanded hybrid"); } @Override protected void traceCreateArray(ClassActor classActor, int length, Pointer cell, Size size) { traceAllocation(classActor, cell, size, length, "array"); } private static void traceAllocation(ClassActor classActor, Pointer cell, Size size, int length, String variant) { Log.print(": Allocated "); Log.print(variant); Log.print(' '); Log.print(classActor.name.string); if (length >= 0) { Log.print(" of length "); Log.print(length); } Log.print(" at "); Log.print(cell); Log.print(" ["); Log.print(size.toInt()); Log.println(" bytes]"); } } // START GENERATED CODE private static abstract class AllocationLoggerAuto extends com.sun.max.vm.log.VMLogger { public enum Operation { Clone, CreateArray, CreateHybrid, CreateTuple, ExpandHybrid; @SuppressWarnings("hiding") public static final Operation[] VALUES = values(); } private static final int[] REFMAPS = null; protected AllocationLoggerAuto(String name, String optionDescription) { super(name, Operation.VALUES.length, optionDescription, REFMAPS); } protected AllocationLoggerAuto() { } @Override public String operationName(int opCode) { return Operation.VALUES[opCode].name(); } @INLINE public final void logClone(ClassActor classActor, Pointer cell, Size size) { log(Operation.Clone.ordinal(), classActorArg(classActor), cell, size); } protected abstract void traceClone(ClassActor classActor, Pointer cell, Size size); @INLINE public final void logCreateArray(ClassActor classActor, int length, Pointer cell, Size size) { log(Operation.CreateArray.ordinal(), classActorArg(classActor), intArg(length), cell, size); } protected abstract void traceCreateArray(ClassActor classActor, int length, Pointer cell, Size size); @INLINE public final void logCreateHybrid(ClassActor classActor, Pointer cell, Size size) { log(Operation.CreateHybrid.ordinal(), classActorArg(classActor), cell, size); } protected abstract void traceCreateHybrid(ClassActor classActor, Pointer cell, Size size); @INLINE public final void logCreateTuple(ClassActor classActor, Pointer cell, Size size) { log(Operation.CreateTuple.ordinal(), classActorArg(classActor), cell, size); } protected abstract void traceCreateTuple(ClassActor classActor, Pointer cell, Size size); @INLINE public final void logExpandHybrid(ClassActor classActor, Pointer cell, Size size) { log(Operation.ExpandHybrid.ordinal(), classActorArg(classActor), cell, size); } protected abstract void traceExpandHybrid(ClassActor classActor, Pointer cell, Size size); @Override protected void trace(Record r) { switch (r.getOperation()) { case 0: { //Clone traceClone(toClassActor(r, 1), toPointer(r, 2), toSize(r, 3)); break; } case 1: { //CreateArray traceCreateArray(toClassActor(r, 1), toInt(r, 2), toPointer(r, 3), toSize(r, 4)); break; } case 2: { //CreateHybrid traceCreateHybrid(toClassActor(r, 1), toPointer(r, 2), toSize(r, 3)); break; } case 3: { //CreateTuple traceCreateTuple(toClassActor(r, 1), toPointer(r, 2), toSize(r, 3)); break; } case 4: { //ExpandHybrid traceExpandHybrid(toClassActor(r, 1), toPointer(r, 2), toSize(r, 3)); break; } } } } private static abstract class RootScanLoggerAuto extends com.sun.max.vm.log.VMLogger { public enum Operation { ScanningBootHeap, VisitReferenceMapSlot; @SuppressWarnings("hiding") public static final Operation[] VALUES = values(); } private static final int[] REFMAPS = null; protected RootScanLoggerAuto(String name, String optionDescription) { super(name, Operation.VALUES.length, optionDescription, REFMAPS); } @Override public String operationName(int opCode) { return Operation.VALUES[opCode].name(); } @INLINE public final void logScanningBootHeap(Address start, Address end, Address mutableReferencesEnd) { log(Operation.ScanningBootHeap.ordinal(), start, end, mutableReferencesEnd); } protected abstract void traceScanningBootHeap(Address start, Address end, Address mutableReferencesEnd); @INLINE public final void logVisitReferenceMapSlot(int regionWordIndex, Pointer address, Word value) { log(Operation.VisitReferenceMapSlot.ordinal(), intArg(regionWordIndex), address, value); } protected abstract void traceVisitReferenceMapSlot(int regionWordIndex, Pointer address, Word value); @Override protected void trace(Record r) { switch (r.getOperation()) { case 0: { //ScanningBootHeap traceScanningBootHeap(toAddress(r, 1), toAddress(r, 2), toAddress(r, 3)); break; } case 1: { //VisitReferenceMapSlot traceVisitReferenceMapSlot(toInt(r, 1), toPointer(r, 2), toWord(r, 3)); break; } } } } // END GENERATED CODE }