| /* |
| * This file is part of the Jikes RVM project (http://jikesrvm.org). |
| * |
| * This file is licensed to You under the Eclipse Public License (EPL); |
| * You may not use this file except in compliance with the License. You |
| * may obtain a copy of the License at |
| * |
| * http://www.opensource.org/licenses/eclipse-1.0.php |
| * |
| * See the COPYRIGHT.txt file distributed with this work for information |
| * regarding copyright ownership. |
| */ |
| package org.mmtk.plan.semispace.gcspy; |
| |
| import org.mmtk.plan.Phase; |
| import org.mmtk.plan.semispace.SSMutator; |
| import org.mmtk.policy.CopySpace; |
| import org.mmtk.policy.Space; |
| import org.mmtk.policy.ImmortalLocal; |
| import org.mmtk.utility.Log; |
| import org.mmtk.utility.alloc.BumpPointer; |
| import org.mmtk.utility.alloc.Allocator; |
| import org.mmtk.utility.gcspy.GCspy; |
| import org.mmtk.utility.gcspy.drivers.LinearSpaceDriver; |
| |
| import org.vmmagic.pragma.*; |
| import org.vmmagic.unboxed.*; |
| |
| /** |
| * This class implements <i>per-mutator thread</i> behavior and state for the |
| * <i>SSGCspy</i> plan. |
| * |
| * See {@link SSGCspy} for an overview of the GC-spy mechanisms. |
| * <p> |
| * |
| * @see SSMutator |
| * @see SSGCspy |
| * @see SSGCspyCollector |
| * @see org.mmtk.plan.StopTheWorldMutator |
| * @see org.mmtk.plan.MutatorContext |
| */ |
| @Uninterruptible public class SSGCspyMutator extends SSMutator { |
| |
| /***************************************************************************** |
| * Instance fields |
| */ |
| |
| private static final boolean DEBUG = false; |
| |
| private static final boolean LOS_TOSPACE = true; // gather from tospace |
| private static final boolean LOS_FROMSPACE = false; // gather from fromspace |
| |
| /** Per-mutator allocator into GCspy's space */ |
| private BumpPointer gcspy = new ImmortalLocal(SSGCspy.gcspySpace); |
| |
| |
| |
| /***************************************************************************** |
| * |
| * Mutator-time allocation |
| */ |
| |
| /** |
| * Allocate space (for an object) |
| * |
| * @param bytes The size of the space to be allocated (in bytes) |
| * @param align The requested alignment. |
| * @param offset The alignment offset. |
| * @param allocator The allocator number to be used for this allocation |
| * @param site Allocation site |
| * @return The address of the first byte of the allocated region |
| */ |
| @Inline |
| public Address alloc(int bytes, int align, int offset, int allocator, int site) { |
| if (allocator == SSGCspy.ALLOC_GCSPY) |
| return gcspy.alloc(bytes, align, offset); |
| else |
| return super.alloc(bytes, align, offset, allocator, site); |
| } |
| |
| /** |
| * Perform post-allocation actions. For many allocators none are required. |
| * |
| * @param object The newly allocated object |
| * @param typeRef The type reference for the instance being created |
| * @param bytes The size of the space to be allocated (in bytes) |
| * @param allocator The allocator number to be used for this allocation |
| */ |
| @Inline |
| public void postAlloc(ObjectReference object, ObjectReference typeRef, |
| int bytes, int allocator) { |
| if (allocator == SSGCspy.ALLOC_GCSPY) |
| SSGCspy.gcspySpace.initializeHeader(object); |
| else |
| super.postAlloc(object, typeRef, bytes, allocator); |
| } |
| |
| /** |
| * Return the allocator instance associated with a space |
| * <code>space</code>, for this plan instance. |
| * |
| * @param space The space for which the allocator instance is desired. |
| * @return The allocator instance associated with this plan instance |
| * which is allocating into <code>space</code>, or <code>null</code> |
| * if no appropriate allocator can be established. |
| */ |
| public Allocator getAllocatorFromSpace(Space space) { |
| if (space == SSGCspy.gcspySpace) return gcspy; |
| return super.getAllocatorFromSpace(space); |
| } |
| |
| /***************************************************************************** |
| * |
| * Collection |
| */ |
| |
| /** |
| * Perform a per-mutator collection phase. |
| * Before a collection, we need to discover |
| * <ul> |
| * <li>the tospace objects copied by the collector in the last GC cycle |
| * <li>the ojects allocated since by the mutator. |
| * <li>all immortal objects allocated by the mutator |
| * <li>all large objects allocated by the mutator |
| * </ul> |
| * After the semispace has been copied, we need to discover |
| * <ul> |
| * <li>the tospace objects copied by the collector |
| * <li>all immortal objects allocated by the mutator |
| * <li>all large objects allocated by the mutator |
| * </ul> |
| */ |
| @Inline |
| public final void collectionPhase(short phaseId, boolean primary) { |
| if (DEBUG) { Log.write("--Phase Mutator."); Log.writeln(Phase.getName(phaseId)); } |
| |
| // TODO do we need to worry any longer about primary?? |
| if (phaseId == SSGCspy.PREPARE) { |
| //if (primary) |
| gcspyGatherData(SSGCspy.BEFORE_COLLECTION); |
| super.collectionPhase(phaseId, primary); |
| return; |
| } |
| |
| if (phaseId == SSGCspy.RELEASE) { |
| //if (primary) |
| gcspyGatherData(SSGCspy.SEMISPACE_COPIED); |
| super.collectionPhase(phaseId, primary); |
| //if (primary) |
| gcspyGatherData(SSGCspy.AFTER_COLLECTION); |
| return; |
| } |
| |
| super.collectionPhase(phaseId, primary); |
| } |
| |
| /** |
| * Gather data for GCspy for the semispaces, the immortal space and the large |
| * object space. |
| * <p> |
| * This method sweeps the semispace under consideration to gather data. |
| * Alternatively and more efficiently, 'used space' can obviously be |
| * discovered in constant time simply by comparing the start and the end |
| * addresses of the semispace. However, per-object information can only be |
| * gathered by sweeping through the space and we do this here for tutorial |
| * purposes. |
| * |
| * @param event |
| * The event, either BEFORE_COLLECTION, SEMISPACE_COPIED or |
| * AFTER_COLLECTION |
| */ |
| private void gcspyGatherData(int event) { |
| if(DEBUG) { |
| Log.writeln("SSGCspyMutator.gcspyGatherData, event=", event); |
| Log.writeln("SSGCspyMutator.gcspyGatherData, port=", GCspy.getGCspyPort()); |
| } |
| |
| // If port = 0 there can be no GCspy client connected |
| if (GCspy.getGCspyPort() == 0) |
| return; |
| |
| // If the server is connected to a client that is interested in this |
| // event, then we gather data. But first we start a timer to |
| // compensate for the time spent gathering data here. |
| if (GCspy.server.isConnected(event)) { |
| |
| if (DEBUG) { |
| if (SSGCspy.hi) |
| Log.write("\nMutator Examining Lowspace (event ", event); |
| else |
| Log.write("\nMutator Examining Highspace (event ", event); |
| Log.write(")"); |
| SSGCspy.reportSpaces(); Log.writeln(); |
| } |
| |
| if (event == SSGCspy.BEFORE_COLLECTION) { |
| // Before the flip |
| // Mutator has not rebound toSpace yet |
| GCspy.server.startCompensationTimer(); |
| |
| // -- Handle the semispaces |
| // Here I need to scan newly allocated objects |
| if (DEBUG) { |
| //debugSpaces(SSGCspy.fromSpace()); |
| debugSpaces(SSGCspy.toSpace()); |
| Log.write("SSGCspyMutator.gcspyGatherData reset, gather and transmit driver "); |
| //Log.writeln(SSGCspy.fromSpace().getName()); |
| Log.writeln(SSGCspy.toSpace().getName()); |
| } |
| //ss.gcspyGatherData(fromSpaceDriver(), SSGCspy.fromSpace()); |
| ss.gcspyGatherData(toSpaceDriver(), SSGCspy.toSpace()); |
| |
| // -- Handle the immortal space -- |
| gatherImmortal(event); |
| |
| // -- Handle the LOSes |
| |
| // reset, collect and scan los data for the nursery and tospace |
| SSGCspy.losNurseryDriver.resetData(); |
| los.gcspyGatherData(event, SSGCspy.losNurseryDriver); |
| SSGCspy.losDriver.resetData(); |
| los.gcspyGatherData(event, SSGCspy.losDriver, LOS_TOSPACE); |
| |
| // transmit the data |
| GCspy.server.stopCompensationTimer(); |
| //fromSpaceDriver().transmit(event); |
| toSpaceDriver().transmit(event); |
| SSGCspy.immortalDriver.transmit(event); |
| SSGCspy.losNurseryDriver.transmit(event); |
| SSGCspy.losDriver.transmit(event); |
| SSGCspy.plosNurseryDriver.transmit(event); |
| SSGCspy.plosDriver.transmit(event); |
| |
| // As this follows Collector.gcspyGatherData, I'll safepoint here |
| // This is a safepoint for the server, i.e. it is a point at which |
| // the server can pause. |
| GCspy.server.serverSafepoint(event); |
| } else if (event == SSGCspy.SEMISPACE_COPIED) { |
| // We have flipped |
| // toSpace still has not been rebound |
| |
| // -- Handle the semispaces |
| if (DEBUG) { |
| //debugSpaces(SSGCspy.toSpace()); |
| debugSpaces(SSGCspy.fromSpace()); |
| Log.writeln("SSGCspyMutator.gcspyGatherData: do nothing"); |
| } |
| |
| // -- Handle the immortal space -- |
| GCspy.server.startCompensationTimer(); |
| gatherImmortal(event); |
| |
| // reset, scan and send the los for the nursery and tospace |
| // and fromspace as well if full heap collection |
| SSGCspy.losNurseryDriver.resetData(); |
| los.gcspyGatherData(event, SSGCspy.losNurseryDriver); |
| SSGCspy.losDriver.resetData(); |
| los.gcspyGatherData(event, SSGCspy.losDriver, LOS_FROMSPACE); |
| los.gcspyGatherData(event, SSGCspy.losDriver, LOS_TOSPACE); |
| |
| // transmit |
| GCspy.server.stopCompensationTimer(); |
| SSGCspy.immortalDriver.transmit(event); |
| SSGCspy.losNurseryDriver.transmit(event); |
| SSGCspy.losDriver.transmit(event); |
| SSGCspy.plosNurseryDriver.transmit(event); |
| SSGCspy.plosDriver.transmit(event); |
| |
| // As this follows Collector.gcspyGatherData, I'll safepoint here |
| // This is a safepoint for the server, i.e. it is a point at which |
| // the server can pause. |
| GCspy.server.serverSafepoint(event); |
| } else if (event == SSGCspy.AFTER_COLLECTION) { |
| // We have flipped |
| // And toSpace has been rebound |
| |
| GCspy.server.startCompensationTimer(); |
| |
| // -- Handle the semispaces |
| if (DEBUG) debugSpaces(SSGCspy.toSpace()); |
| |
| // -- Handle the immortal space -- |
| gatherImmortal(event); |
| |
| // -- Handle the LOSes |
| |
| // reset, scan and send the los |
| SSGCspy.losNurseryDriver.resetData(); |
| SSGCspy.losDriver.resetData(); |
| // no need to scan empty nursery |
| los.gcspyGatherData(event, SSGCspy.losDriver, LOS_TOSPACE); |
| |
| //transmit |
| GCspy.server.stopCompensationTimer(); |
| SSGCspy.immortalDriver.transmit(event); |
| SSGCspy.losNurseryDriver.transmit(event); |
| SSGCspy.losDriver.transmit(event); |
| SSGCspy.plosNurseryDriver.transmit(event); |
| SSGCspy.plosDriver.transmit(event); |
| |
| // Reset fromspace |
| if (DEBUG) { |
| Log.write("SSGCspyMutator.gcspyGatherData: reset and zero range for driver "); |
| Log.write(SSGCspy.toSpace().getName()); |
| } |
| } |
| |
| } |
| // else Log.write("not transmitting..."); |
| } |
| |
| /** |
| * Gather data for the immortal space |
| * @param event |
| * The event, either BEFORE_COLLECTION, SEMISPACE_COPIED or |
| * AFTER_COLLECTION |
| */ |
| private void gatherImmortal(int event) { |
| // We want to do this at every GCspy event |
| if (DEBUG) { |
| Log.write("SSGCspyMutator.gcspyGatherData: gather data for immortal space "); |
| Log.write(SSGCspy.immortalSpace.getStart()); Log.writeln("-",immortal.getCursor()); |
| } |
| SSGCspy.immortalDriver.resetData(); |
| immortal.gcspyGatherData(SSGCspy.immortalDriver); |
| if (DEBUG) Log.writeln("Finished immortal space."); |
| } |
| |
| /** |
| * Debugging info for the semispaces |
| * @param scannedSpace the space to output debug for. |
| */ |
| private void debugSpaces(CopySpace scannedSpace) { |
| Log.write("SSGCspyMutator.gcspyGatherData: gather data for active semispace "); |
| Log.write(scannedSpace.getStart()); Log.write("-",ss.getCursor()); Log.flush(); |
| Log.write(". The space is: "); Log.writeln(ss.getSpace().getName()); |
| Log.write("scannedSpace is "); Log.writeln(scannedSpace.getName()); |
| Log.write("The range is "); Log.write(ss.getSpace().getStart()); |
| Log.write(" to "); Log.writeln(ss.getCursor()); |
| SSGCspy.reportSpaces(); |
| } |
| |
| /** @return the driver for toSpace */ |
| private LinearSpaceDriver toSpaceDriver() { |
| return SSGCspy.hi ? SSGCspy.ss1Driver : SSGCspy.ss0Driver; |
| } |
| |
| } |