| /* |
| * 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 org.mmtk.policy.Space; |
| import org.mmtk.utility.gcspy.Color; |
| import org.mmtk.utility.gcspy.LinearScan; |
| import org.mmtk.utility.gcspy.StreamConstants; |
| import org.mmtk.utility.gcspy.Subspace; |
| import org.mmtk.vm.gcspy.IntStream; |
| import org.mmtk.vm.gcspy.ShortStream; |
| |
| import org.mmtk.utility.Log; |
| import org.mmtk.vm.gcspy.ServerInterpreter; |
| import org.mmtk.vm.VM; |
| |
| import org.vmmagic.unboxed.*; |
| import org.vmmagic.pragma.*; |
| |
| /** |
| * GCspy driver for the MMTk ContigousSpace. |
| * |
| * This class implements a simple driver for contiguous MMTk spaces |
| * such as CopySpace and ImmortalSpace. |
| */ |
| @Uninterruptible public class LinearSpaceDriver extends AbstractDriver { |
| |
| // The GCspy streams |
| protected IntStream scalarUsedSpaceStream; |
| protected IntStream arrayUsedSpaceStream; |
| protected ShortStream scalarObjectsStream; |
| protected ShortStream arrayObjectsStream; |
| protected ShortStream arrayPrimitiveStream; |
| protected ShortStream rootsStream; |
| protected ShortStream refFromImmortalStream; |
| |
| protected Subspace subspace; // A subspace for all of this space |
| protected int allTileNum; // total number of tiles |
| |
| // Overall statistics |
| protected int totalScalarObjects = 0; // total number of objects allocated |
| protected int totalArrayObjects = 0; |
| protected int totalPrimitives = 0; |
| protected int totalScalarUsedSpace = 0; // total space used |
| protected int totalArrayUsedSpace = 0; |
| protected int totalRoots = 0; |
| protected int totalRefFromImmortal = 0; |
| |
| private final LinearScan scanner; // A scanner to trace objects |
| |
| // Debugging |
| protected Address lastAddress = Address.zero(); |
| protected int lastSize = 0; |
| private static final boolean DEBUG = false; |
| |
| |
| /** |
| * Create a new driver for a contiguous MMTk space. |
| * |
| * @param server The GCspy ServerInterpreter |
| * @param spaceName The name of this GCspy space |
| * @param mmtkSpace The MMTk space |
| * @param blockSize The tile size |
| * @param mainSpace Is this the main space? |
| */ |
| public LinearSpaceDriver(ServerInterpreter server, |
| String spaceName, |
| Space mmtkSpace, |
| int blockSize, |
| boolean mainSpace) { |
| |
| super(server, spaceName, mmtkSpace, blockSize, mainSpace); |
| |
| if (DEBUG) { |
| Log.write("LinearSpaceDriver for "); Log.write(spaceName); |
| Log.write(", blocksize="); Log.write(blockSize); |
| Log.write(", start="); Log.write(mmtkSpace.getStart()); |
| Log.write(", extent="); Log.write(mmtkSpace.getExtent()); |
| Log.write(", maxTileNum="); Log.writeln(maxTileNum); |
| } |
| |
| // Initialise a subspace and 4 Streams |
| subspace = createSubspace(mmtkSpace); |
| allTileNum = 0; |
| scalarUsedSpaceStream = createScalarUsedSpaceStream(); |
| arrayUsedSpaceStream = createArrayUsedSpaceStream(); |
| scalarObjectsStream = createScalarObjectsStream(); |
| arrayPrimitiveStream = createArrayPrimitiveStream(); |
| arrayObjectsStream = createArrayObjectsStream(); |
| rootsStream = createRootsStream(); |
| refFromImmortalStream = createRefFromImmortalStream(); |
| serverSpace.resize(0); // the collector must call resize() before gathering data |
| |
| // Initialise the statistics |
| resetData(); |
| scanner = new LinearScan(this); |
| } |
| |
| /** |
| * Get the name of this driver type. |
| * @return The name of this driver. |
| */ |
| protected String getDriverName() { return "MMTk LinearSpaceDriver"; } |
| |
| /** |
| * Private creator methods to create the Streams. |
| */ |
| @Interruptible |
| private IntStream createScalarUsedSpaceStream() { |
| return VM.newGCspyIntStream( |
| this, |
| "Scalar Used Space stream", // stream name |
| 0, // min. data value |
| blockSize, // max. data value |
| 0, // zero value |
| 0, // default value |
| "Scalars and primitive arrays: ", // value prefix |
| " bytes", // value suffix |
| StreamConstants.PRESENTATION_PERCENT, // presentation style |
| StreamConstants.PAINT_STYLE_ZERO, // paint style |
| 0, // index of max stream (only needed if the presentation is *_VAR) |
| Color.Red, // tile colour |
| true); // summary enabled |
| } |
| |
| @Interruptible |
| private IntStream createArrayUsedSpaceStream() { |
| return VM.newGCspyIntStream( |
| this, |
| "Array Used Space stream", |
| 0, |
| blockSize, |
| 0, |
| 0, |
| "Reference arrays: ", |
| " bytes", |
| StreamConstants.PRESENTATION_PERCENT, |
| StreamConstants.PAINT_STYLE_ZERO, |
| 0, |
| Color.Blue, |
| true); |
| } |
| |
| @Interruptible |
| private ShortStream createScalarObjectsStream() { |
| return VM.newGCspyShortStream( |
| this, |
| "Scalar Objects stream", |
| (short)0, |
| // Say, max value = 50% of max possible |
| (short)(maxObjectsPerBlock(blockSize)/2), |
| (short)0, |
| (short)0, |
| "Scalars: ", |
| " objects", |
| StreamConstants.PRESENTATION_PLUS, |
| StreamConstants.PAINT_STYLE_ZERO, |
| 0, |
| Color.Green, |
| true); |
| } |
| |
| @Interruptible |
| private ShortStream createArrayPrimitiveStream() { |
| return VM.newGCspyShortStream( |
| this, |
| "Array Primitive stream", |
| (short)0, |
| // Say, typical primitive array size = 4 * typical scalar size? |
| (short)(maxObjectsPerBlock(blockSize)/8), |
| (short)0, |
| (short)0, |
| "Primitive arrays: ", |
| " objects", |
| StreamConstants.PRESENTATION_PLUS, |
| StreamConstants.PAINT_STYLE_ZERO, |
| 0, |
| Color.Yellow, |
| true); |
| } |
| |
| @Interruptible |
| private ShortStream createArrayObjectsStream() { |
| return VM.newGCspyShortStream( |
| this, |
| "Array Objects stream", |
| (short)0, |
| // Say, typical ref array size = 4 * typical scalar size? |
| (short)(maxObjectsPerBlock(blockSize)/8), |
| (short)0, |
| (short)0, |
| "Reference arrays: ", |
| " objects", |
| StreamConstants.PRESENTATION_PLUS, |
| StreamConstants.PAINT_STYLE_ZERO, |
| 0, |
| Color.Cyan, |
| true); |
| } |
| |
| @Interruptible |
| private ShortStream createRootsStream() { |
| return VM.newGCspyShortStream( |
| this, |
| "Roots stream", |
| (short)0, |
| // Say, typical size = 4 * typical scalar size? |
| (short)(maxObjectsPerBlock(blockSize)/8), |
| (short)0, |
| (short)0, |
| "Roots: ", |
| " objects", |
| StreamConstants.PRESENTATION_PLUS, |
| StreamConstants.PAINT_STYLE_ZERO, |
| 0, |
| Color.Blue, |
| true); |
| } |
| |
| @Interruptible |
| private ShortStream createRefFromImmortalStream() { |
| return VM.newGCspyShortStream( |
| this, |
| "References from immortal stream", |
| (short)0, |
| // Say, typical size = 4 * typical scalar size? |
| (short)(maxObjectsPerBlock(blockSize)/8), |
| (short)0, |
| (short)0, |
| "References from immortal space: ", |
| " references", |
| StreamConstants.PRESENTATION_PLUS, |
| StreamConstants.PAINT_STYLE_ZERO, |
| 0, |
| Color.Blue, |
| true); |
| } |
| |
| /** |
| * Reset the statistics for all the streams, including totals used for summaries |
| */ |
| public void resetData() { |
| super.resetData(); |
| |
| // Reset all the streams |
| scalarUsedSpaceStream.resetData(); |
| arrayUsedSpaceStream.resetData(); |
| scalarObjectsStream.resetData(); |
| arrayObjectsStream.resetData(); |
| arrayPrimitiveStream.resetData(); |
| refFromImmortalStream.resetData(); |
| |
| // Reset the summary counts |
| totalScalarObjects = 0; |
| totalArrayObjects = 0; |
| totalPrimitives = 0; |
| totalScalarUsedSpace = 0; |
| totalArrayUsedSpace = 0; |
| totalRefFromImmortal = 0; |
| } |
| |
| /** |
| * BumpPointer.linearScan needs a LinearScan object, which we provide here. |
| * @return the scanner for this driver |
| */ |
| public LinearScan getScanner() { return scanner; } |
| |
| /** |
| * Set the current address range of a contiguous space |
| * @param start the start of the contiguous space |
| * @param end the end of the contiguous space |
| */ |
| public void setRange(Address start, Address end) { |
| int current = subspace.getBlockNum(); |
| int required = countTileNum(start, end, subspace.getBlockSize()); |
| |
| // Reset the subspace |
| if(required != current) |
| subspace.reset(start, end, 0, required); |
| |
| if (DEBUG) { |
| Log.write("\nContiguousSpaceDriver.setRange for contiguous space: "); |
| Log.write(subspace.getFirstIndex()); Log.write("-", subspace.getBlockNum()); |
| Log.write(" (", start); Log.write("-", end); Log.write(")"); |
| } |
| |
| // Reset the driver |
| // Note release() only resets a CopySpace's cursor (and optionally zeroes |
| // or mprotects the pages); it doesn't make the pages available to other |
| // spaces. If pages were to be released, change the test here to |
| // if (allTileNum != required) { |
| if (allTileNum < required) { |
| if (DEBUG) { Log.write(", resize from ", allTileNum); Log.write(" to ", required); } |
| allTileNum = required; |
| serverSpace.resize(allTileNum); |
| setTilenames(subspace, allTileNum); |
| } |
| if (DEBUG) Log.writeln(); |
| } |
| |
| |
| /** |
| * Update the tile statistics |
| * @param obj The current object |
| */ |
| public void scan(ObjectReference obj) { |
| scan(obj, true); |
| } |
| |
| /** |
| * Update the tile statistics |
| * @param obj The current object |
| * @param total Whether to accumulate the values |
| */ |
| public void scan(ObjectReference obj, boolean total) { |
| boolean isArray = VM.objectModel.isArray(obj); |
| int length = VM.objectModel.getCurrentSize(obj); |
| Address addr = obj.toAddress(); |
| |
| if (VM.VERIFY_ASSERTIONS) { |
| if(addr.LT(lastAddress.plus(lastSize))) { |
| Log.write("\nContiguousSpaceDriver finds addresses going backwards: "); |
| Log.write("last="); Log.write(lastAddress); |
| Log.write(" last size="); Log.write(lastSize); |
| Log.writeln(" current=", addr); |
| } |
| lastAddress = addr; |
| lastSize = length; |
| } |
| |
| // Update the stats |
| if (subspace.addressInRange(addr)) { |
| int index = subspace.getIndex(addr); |
| int remainder = subspace.spaceRemaining(addr); |
| if (isArray) { |
| arrayObjectsStream.increment(index, (short)1); |
| arrayUsedSpaceStream.distribute(index, remainder, blockSize, length); |
| if (total) { |
| totalArrayObjects++; |
| totalArrayUsedSpace += length; |
| } |
| } else { |
| if(!this.scanCheckPrimitiveArray(obj, index, total, length)) { |
| // real object |
| scalarObjectsStream.increment(index, (short)1); |
| if (total) { |
| totalScalarObjects++; |
| totalScalarUsedSpace += length; |
| } |
| } |
| scalarUsedSpaceStream.distribute(index, remainder, blockSize, length); |
| } |
| } |
| } |
| |
| /** |
| * Check if this Object is an array of primitives.<br> |
| * Part of the public scan() method. |
| * |
| * @param obj The Object to check |
| * @param index Index of the tile |
| * @param total Increment summary |
| * @param length Current size of the Object, will be added to array space summary. |
| * @return True if this Object is an array of primitives. |
| */ |
| protected boolean scanCheckPrimitiveArray(ObjectReference obj, int index, boolean total, int length) { |
| if(VM.objectModel.isPrimitiveArray(obj)) { |
| arrayPrimitiveStream.increment(index, (short)1); |
| if (total) { |
| totalPrimitives++; |
| totalScalarUsedSpace += length; |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Transmit the data if this event is of interest to the client.<p> |
| * Implemented using the algorithm pattern, subclasses can override parts of it. |
| * @param event The event, defined in the Plan |
| */ |
| public void transmit(int event) { |
| if (!server.isConnected(event)) |
| return; |
| |
| if (DEBUG) { |
| Log.write("CONNECTED\n"); |
| Log.write(myClass); |
| Log.write(".send: numTiles=", allTileNum); |
| //Log.write("LinearSpaceDriver.transmit: numTiles=", allTileNum); |
| Log.writeln(", control.length=", control.length); |
| Log.flush(); |
| } |
| |
| // Setup the summaries |
| setupSummaries(); |
| |
| // Setup the control info |
| setupControlInfo(); |
| |
| // Setup the space info |
| Offset size = subspace.getEnd().diff(subspace.getStart()); |
| setSpaceInfo(size); |
| |
| // Send the all streams |
| send(event, allTileNum); |
| |
| // Debugging |
| if (VM.VERIFY_ASSERTIONS) { |
| lastAddress = Address.zero(); |
| lastSize = 0; |
| } |
| } |
| |
| /** |
| * Setup summaries part of the <code>transmit</code> method.<p> |
| * Override this method to setup summaries of additional streams in subclasses. |
| */ |
| protected void setupSummaries() { |
| scalarUsedSpaceStream.setSummary(totalScalarUsedSpace, |
| subspace.getEnd().diff(subspace.getStart()).toInt()); |
| arrayUsedSpaceStream.setSummary(totalArrayUsedSpace, |
| subspace.getEnd().diff(subspace.getStart()).toInt()); |
| scalarObjectsStream.setSummary(totalScalarObjects); |
| arrayObjectsStream.setSummary(totalArrayObjects); |
| arrayPrimitiveStream.setSummary(totalPrimitives); |
| rootsStream.setSummary(totalRoots); |
| refFromImmortalStream.setSummary(totalRefFromImmortal); |
| } |
| |
| /** |
| * Setup control info part of the <code>transmit</code> method.<p> |
| * Override this method to change the controls for your own driver subclass. |
| */ |
| protected void setupControlInfo() { |
| int numBlocks = subspace.getBlockNum(); |
| controlValues(CONTROL_USED, subspace.getFirstIndex(), numBlocks); |
| if (DEBUG) { |
| Log.write("LinearSpaceDriver.transmitSetupControlInfo: allTileNum=", allTileNum); |
| Log.writeln(", numBlocks=", numBlocks); |
| } |
| if (numBlocks < allTileNum) |
| controlValues(CONTROL_UNUSED, |
| subspace.getFirstIndex() + numBlocks, |
| allTileNum - numBlocks); |
| } |
| |
| /** |
| * Handle a root address |
| * |
| * @param addr Root Address |
| * @return true if the given Address is in this subspace. |
| */ |
| public boolean handleRoot(Address addr) { |
| if(subspace.addressInRange(addr)) { |
| // increment tile |
| int index = subspace.getIndex(addr); |
| rootsStream.increment(index, (short)1); |
| // increment summary |
| this.totalRoots++; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Reset the roots Stream |
| * The roots Stream has to be reset seperately because we do not |
| * gather data in the usual way using <code>scan()</code>. |
| */ |
| public void resetRootsStream() { |
| rootsStream.resetData(); |
| totalRoots = 0; |
| } |
| |
| /** |
| * Handle a direct reference from the immortal space. |
| * |
| * @param addr The Address |
| * @return true if the given Address is in this subspace. |
| */ |
| public boolean handleReferenceFromImmortalSpace(Address addr) { |
| if(subspace.addressInRange(addr)) { |
| // increment tile |
| int index = subspace.getIndex(addr); |
| refFromImmortalStream.increment(index, (short)1); |
| // increment summary |
| this.totalRefFromImmortal++; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| } |