/*
* 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.
*
* @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 final BumpPointer gcspy = new ImmortalLocal(SSGCspy.gcspySpace);
/*****************************************************************************
*
* Mutator-time allocation
*/
/**
* {@inheritDoc}
*/
@Override
@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);
}
@Override
@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);
}
@Override
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>
*/
@Override
@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;
}
}