/* * 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.utility.gcspy.drivers; import static org.mmtk.utility.Constants.MAX_INT; import org.mmtk.policy.Space; import org.mmtk.utility.Log; import org.mmtk.utility.gcspy.GCspy; import org.mmtk.utility.gcspy.Subspace; import org.mmtk.vm.gcspy.ServerSpace; import org.mmtk.vm.gcspy.ServerInterpreter; import org.mmtk.vm.gcspy.Stream; import org.mmtk.vm.VM; import org.vmmagic.unboxed.*; import org.vmmagic.pragma.*; /** * Abstract GCspy driver for MMTk collectors.<p> * * This class implements for the MMTk a base driver for a GCspy space. * All drivers for GCspy spaces should inherit from this class. */ @Uninterruptible public abstract class AbstractDriver { /**************************************************************************** * * Class variables */ // Controls used for tile presentation /** The tile is used */ protected static final byte CONTROL_USED = 1; /** The tile is a background tile */ protected static final byte CONTROL_BACKGROUND = 2; /** The tile is unused */ protected static final byte CONTROL_UNUSED = 4; /** The tile is a separator */ protected static final byte CONTROL_SEPARATOR = 8; /** The tile is a link */ protected static final byte CONTROL_LINK = 16; private static final int MAX_STREAMS = 64; // Max number of streams private static final boolean DEBUG = false; protected String myClass; // used in debugging messages /**************************************************************************** * * Instance variables */ /** The owning GCspy server */ protected final ServerInterpreter server; /** The name of the GCspy space driver */ protected final String name; /** The GCspy space abstraction */ protected final ServerSpace serverSpace; /** The MMTK space */ protected final Space mmtkSpace; /** The GCspy space's block size */ protected int blockSize; /** The maximum number of tiles in this GCspy space */ protected int maxTileNum; /** This space's streams */ protected Stream[] streams; /** control values for tiles in this space */ protected byte[] control; /** Has this space changed? */ protected boolean changed = true; /** * Create a new driver for this collector. * * @param server The ServerInterpreter that owns this GCspy space. * @param name The name of this driver. * @param mmtkSpace The MMTk space represented by this driver. * @param blockSize The tile size. * @param mainSpace Is this the main space? */ public AbstractDriver(ServerInterpreter server, String name, Space mmtkSpace, int blockSize, boolean mainSpace) { this.server = server; this.name = name; this.mmtkSpace = mmtkSpace; this.blockSize = blockSize; myClass = getClass().getName(); maxTileNum = countTileNum(mmtkSpace.getExtent(), blockSize); control = (byte[])GCspy.util.createDataArray(new byte[0], maxTileNum); // to avoid allocation during GC we preallocate the streams array streams = new Stream[MAX_STREAMS]; serverSpace = createServerSpace(server, name, maxTileNum, mainSpace); } /** * Creates a subspace for this space. * Subspace provide useful facilities for contiguous spaces, even if * a space contains only one. * @param mmtkSpace The MMTk space * @return the created subspace */ @Interruptible protected Subspace createSubspace(Space mmtkSpace) { Address start = mmtkSpace.getStart(); return new Subspace(start, start, 0, blockSize, 0); } /** * Create a new GCspy ServerSpace and add it to the ServerInterpreter. * @param server the GCspy ServerInterpreter. * @param spaceName The name of this driver. * @param maxTileNum the maximum number of tiles in this space. * @param mainSpace Is this the main space? * @return the created server space */ @Interruptible protected ServerSpace createServerSpace(ServerInterpreter server, String spaceName, int maxTileNum, boolean mainSpace) { // Set the block label String tmp = "Block Size: " + ((blockSize < 1024) ? blockSize + " bytes\n" : (blockSize / 1024) + " Kbytes\n"); // Create a single GCspy Space return VM.newGCspyServerSpace(server, // the server spaceName, // space name getDriverName(), // driver (space) name "Block ", // space title tmp, // block info maxTileNum, // number of tiles "UNUSED", // the label for unused blocks mainSpace); // main space } /** * Get the name of this driver type. * @return The name of this driver. */ protected abstract String getDriverName(); /** * Get the maximum number of tiles in this space. * @return the maximum number of tiles in the space. */ public int getMaxTileNum() { return maxTileNum; } /** * The GCspy space managed by this driver. * @return the GCspy server space. */ public ServerSpace getServerSpace() { return serverSpace; } /** * Add a stream to the driver. This also sets the stream's id * (unique for this space). * @param stream The stream * @exception IndexOutOfBoundsException if more than MAX_STREAMS are added */ @Interruptible public void addStream(Stream stream) { int id = 0; while (id < MAX_STREAMS) { if (streams[id] == null) { streams[id] = stream; if (DEBUG) { Log.write("Adding stream with id="); Log.writeln(id); } Address stream_ = serverSpace.addStream(id); stream.setStream(id, stream_); return; } id++; } throw new IndexOutOfBoundsException("Too many streams added to driver " + name); } /** * Count number of tiles in an address range. * @param start The start of the range. * @param end The end of the range. * @param tileSize The size of each tile. * @return The number of tiles in this range. */ protected int countTileNum(Address start, Address end, int tileSize) { if (end.LE(start)) return 0; int diff = end.diff(start).toInt(); return countTileNum(diff, tileSize); } /** * Count number of tiles in an address range. * @param extent The extent of the range. * @param tileSize The size of each tile. * @return The number of tiles in this range. */ protected int countTileNum(Extent extent, int tileSize) { long diff = extent.toLong(); return countTileNum(diff, tileSize); } private int countTileNum(long diff, int tileSize) { long tiles = diff / tileSize; if ((diff % tileSize) != 0) ++tiles; if (tiles > MAX_INT) { Log.writeln("AbstractDriver: tiles does not fit in int"); } return (int)tiles; } /** * Indicate the limits of a space. * * @param start the Address of the start of the space. * @param end the Address of the end of the space. */ public void setRange(Address start, Address end) {} /** * Indicate the limits of a space. * * @param start the Address of the start of the space. * @param extent the extent of the space. */ public void setRange(Address start, Extent extent) { setRange(start, start.plus(extent)); } /** * Setup the tile names in a subspace. Tile names are typically * address ranges but may be anything (e.g. a size class if the * space is a segregated free-list manager, or a class name if the * space represents the class instances loaded). * * @param subspace the Subspace * @param numTiles the number of tiles to name */ protected void setTilenames(Subspace subspace, int numTiles) { Address start = subspace.getStart(); int first = subspace.getFirstIndex(); int bs = subspace.getBlockSize(); for (int i = 0; i < numTiles; ++i) { if (subspace.indexInRange(i)) serverSpace.setTilename(i, start.plus((i - first) * bs), start.plus((i + 1 - first) * bs)); } } /** * The "typical" maximum number of objects in each tile. * @param blockSize The size of a tile * @return The maximum number of objects in a tile */ public int maxObjectsPerBlock(int blockSize) { // Maybe a misuse of ServerInterpreter but it's a convenient // VM-dependent class return blockSize / GCspy.server.computeHeaderSize(); } /** * @param event The current event * @return whether the server is connected to a GCspy client */ public boolean isConnected(int event) { return server.isConnected(event); } /** * Reset the statistics for a space. * In this base driver, we simply note that the data has changed. */ protected void resetData() { changed = true; } /** * Scan an object found at a location. * Collectors typically call this method to update GCspy statistics. * The driver may or may not accumulate values found, depending on * the value of total. * @param obj the reference to the object found * @param total Whether to total the statistics */ public void scan(ObjectReference obj, boolean total) {} /** * Scan an object found at a location. * Collectors typically call this method to update GCspy statistics * The driver will accumulate values found. * @param obj the reference to the object found */ public void scan(ObjectReference obj) { scan(obj, true); } /** * Scan an object found at a location. * Collectors typically call this method to update GCspy statistics. * The driver may or may not accumulate values found, depending on * the value of total. * @param obj the reference to the object found * @param total Whether to total the statistics */ public void scan(Address obj, boolean total) {} /** * Scan an object found at a location. * Collectors typically call this method to update GCspy statistics * The driver will accumulate values found. * @param obj the reference to the object found */ public void scan(Address obj) {} /** * Handle a direct reference from the immortal space.<p> * This is an empty implementation. Subclasses may override this method * to increment their <code>refFromImmortal</code> Stream. * * @param addr The Address * @return {@code true} if the given Address is in this subspace. Always {@code false} here. */ public boolean handleReferenceFromImmortalSpace(Address addr) { return false; } /** * Set space info. * This simply reports the size of the current space. * Drivers that want to send something more complex than * "Current Size: size\n" * must override this method. * * @param size the size of the space */ protected void setSpaceInfo(Offset size) { // - sprintf(tmp, "Current Size: %s\n", gcspy_formatSize(size)); Address tmp = GCspy.util.formatSize("Current Size: %s\n", 128, size.toInt()); serverSpace.spaceInfo(tmp); GCspy.util.free(tmp); } /**************************************************************************** * * Control values */ /** * Is a tile used? * @param val the control value. * @return {@code true} if the tile is used */ protected static boolean controlIsUsed(byte val) { return (val & CONTROL_USED) != 0; } /** * Is a tile a background pseudo-tile? * @param val the control value. * @return {@code true} if the tile is a background tile */ protected static boolean controlIsBackground(byte val) { return (val & CONTROL_BACKGROUND) != 0; } /** * Is a tile unused? * @param val the control value. * @return {@code true} if the tile is unused */ protected static boolean controlIsUnused(byte val) { return (val & CONTROL_UNUSED) != 0; } /** * Is this a separator? * @param val the control value. * @return {@code true} if this is a separator */ protected static boolean controlIsSeparator(byte val) { return (val & CONTROL_SEPARATOR) != 0; } /** * Initialise the value of a control. * @param index The index of the tile. * @param value The new value of the control */ protected void initControl(int index, byte value) { control[index] = value; } /** * Add a control to the tile * @param index The index of the tile. * @param value The control to add. */ protected void addControl(int index, byte value) { control[index] |= value; } /** Set the control * @param index the index of the tile * @param value The value to set */ protected void setControl(int index, byte value) { control[index] &= value; } /** * Get the controls for a tile. * @param index The index of the tile. * @return The value of the controls */ public byte getControl(int index) { return control[index]; } /** * Initialise control values in all tiles */ protected void initControls() { for (int index = 0; index < control.length; ++index) { initControl(index, CONTROL_USED); } } /** * Set the control value in each tile in a region. * @param tag The control tag. * @param start The start index of the region. * @param len The number of tiles in the region. */ protected void controlValues(byte tag, int start, int len) { if (DEBUG) { Log.write("AbstractDriver.controlValues for space "); Log.write(name); Log.write(", control length=", control.length); Log.write(" writing controls from ", start); Log.writeln(" to ", start + len); } changed = true; for (int i = start; i < (start + len); ++i) { // Cannot be both USED and UNUSED or BACKGROUND if (controlIsBackground(tag) || controlIsUnused(tag)) setControl(i, (byte)~CONTROL_USED); else if (controlIsUsed(tag)) setControl(i, (byte)~CONTROL_UNUSED); addControl(i, tag); } } /** * Transmit the streams for this space. A driver will typically * <ol> * <li> Determine whether a GCspy client is connected and interested in * this event, e.g. * <pre>server.isConnected(event)</pre> * <li> Setup the summaries for each stream, e.g. * <pre>stream.setSummary(values...);</pre> * <li> Setup the control information for each tile. e.g. * <pre>controlValues(CONTROL_USED, start, numBlocks);</pre> * <pre>controlValues(CONTROL_UNUSED, end, remainingBlocks);</pre> * <li> Set up the space information, e.g. * <pre>setSpace(info);</pre> * <li> Send the data for all streams, e.g. * <pre>send(event, numTiles);</pre> * Note that AbstractDriver.send takes care of sending the information * for all streams (including control data). * </ol> * * @param event The event */ public abstract void transmit(int event); /** * Send all the streams for this space if it has changed. * Assume that the data has been gathered and that summary info * and control values have been set before this is called. * * @param event the event * @param numTiles the number of blocks in this space */ protected void send(int event, int numTiles) { if (changed) { serverSpace.startCommunication(); for (int i = 0; i < MAX_STREAMS; i++) if (streams[i] != null) streams[i].send(event, numTiles); serverSpace.sendControls(this, numTiles); serverSpace.endCommunication(); } } }