001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.mmtk.plan.semispace.gcspy;
014
015import org.mmtk.plan.GCspyPlan;
016import org.mmtk.plan.Phase;
017import org.mmtk.plan.TransitiveClosure;
018import org.mmtk.plan.semispace.SS;
019import org.mmtk.policy.CopySpace;
020import org.mmtk.policy.LargeObjectSpace;
021import org.mmtk.utility.gcspy.drivers.AbstractDriver;
022import org.mmtk.utility.gcspy.drivers.LinearSpaceDriver;
023import org.mmtk.utility.gcspy.drivers.ImmortalSpaceDriver;
024import org.mmtk.utility.gcspy.drivers.TreadmillDriver;
025import org.mmtk.utility.gcspy.GCspy;
026import org.mmtk.utility.Log;
027import org.mmtk.utility.options.Options;
028
029import org.vmmagic.pragma.*;
030
031/**
032 * This class extends a simple semi-space collector to instrument it for
033 * GCspy. <p>
034 *
035 * See the Jones &amp; Lins GC book, section 2.2 for an overview of the basic
036 * algorithm. This implementation also includes a large object space
037 * (LOS), and an uncollected "immortal" space.<p>
038 *
039 * All plans make a clear distinction between <i>global</i> and
040 * <i>thread-local</i> activities.  Global activities must be
041 * synchronized, whereas no synchronization is required for
042 * thread-local activities.  Instances of Plan map 1:1 to "kernel
043 * threads" (aka CPUs).  Thus instance
044 * methods allow fast, unsychronized access to Plan utilities such as
045 * allocation and collection.  Each instance rests on static resources
046 * (such as memory and virtual memory resources) which are "global"
047 * and therefore "static" members of Plan.  This mapping of threads to
048 * instances is crucial to understanding the correctness and
049 * performance proprties of this plan.
050 * <p>
051 * FIXME This seems to have changed
052 * The order of phases and GCspy actions is important here. It is:
053 * <pre>
054 *   PREPARE phase
055 *      SSGCspyMutator.gcspyGatherData(SSGCspy.BEFORE_COLLECTION); // safepoint
056 *      SSMutator.PREPARE // FIXME DOES NOT ss.rebind(SS.toSpace());
057 *
058 *   PREPARE phase
059 *      SS.PREPARE // flip semispaces
060 *      gcspySpace.prepare();
061 *      SSGCspyCollector.gcspyGatherData(SSGCspy.BEFORE_COLLECTION);
062 *      SSCollector.PREPARE // ss.rebind(SS.toSpace());
063 *
064 *
065 *   FORWARD_FINALIZABLE phase
066 *      SSCollector.FORWARD_FINALIZABLE
067 *      SSGCspyCollector.gcspyGatherData(SSGCspy.SEMISPACE_COPIED);
068 *
069 *   RELEASE phase
070 *      SSGCspyMutator.gcspyGatherData(SSGCspy.SEMISPACE_COPIED); // safepoint
071 *      SSMutator.RELEASE // FIXME ss.rebind(SS.toSpace());
072 *      SSGCspyMutator.gcspyGatherData(SSGCspy.AFTER_COLLECTION);
073 *
074 *   RELEASE phase
075 *      SSCollector.RELEASE
076 *      SSGCspyCollector.gcspyGatherData(SSGCspy.AFTER_COLLECTION);
077 *      SS.RELEASE
078 *      gcspySpace.release();
079 *      SSGCspy.gcspyGatherData(); // safepoint
080 *</pre>
081 * Note that SSMutator has changed the point at which it rebinds toSpace
082 * from PREPARE (2.4.6) to after RELEASE (3.x.x).
083 *<pre>
084 --Phase Collector.initiate
085 --Phase Mutator.initiate-mutator
086 --Phase Mutator.prepare-mutator
087     SSGCspyMutator.gcspyGatherData, event=0
088 --Phase Plan.prepare
089 --Phase Collector.prepare
090     SSGCspyCollector.gcspyGatherData, event=0
091 --Phase Collector.bootimage-root
092 --Phase Collector.root
093 --Phase Plan.root
094 --Phase Collector.start-closure
095 --Phase Collector.soft-ref
096 --Phase Collector.complete-closure
097 --Phase Collector.weak-ref
098 --Phase Collector.finalize
099 --Phase Collector.complete-closure
100 --Phase Collector.phantom-ref
101 --Phase Collector.forward-ref
102 --Phase Collector.forward-finalize
103     SSGCspyCollector.gcspyGatherData, event=1
104 --Phase Mutator.release-mutator
105     SSGCspyMutator.gcspyGatherData, event=1
106     SSGCspyMutator.gcspyGatherData, event=2
107 --Phase Collector.release
108     SSGCspyCollector.gcspyGatherData, event=2
109 --Phase Plan.release
110     SSGCspy.gcspyGatherData, event=2
111 --Phase Collector.complete
112 --Phase Plan.complete
113 </pre>
114 */
115@Uninterruptible public class SSGCspy extends SS implements GCspyPlan {
116
117  /****************************************************************************
118   *
119   * Class variables
120   */
121
122  // The events, BEFORE_COLLECTION, SEMISPACE_COPIED or AFTER_COLLECTION
123
124  /**
125   * Before a collection has started,
126   * i.e. before SS.collectionPhase(SS.PREPARE,..).
127   */
128  static final int BEFORE_COLLECTION = 0;
129
130  /**
131   * After the semispace has been copied and the large object space has been traced
132   * At this time the Large Object Space has not been swept.
133   */
134  static final int SEMISPACE_COPIED  = BEFORE_COLLECTION + 1;
135
136  /**
137   * The collection is complete,
138   * i.e. immediately after SS.collectionPhase(SS.RELEASE,..).
139   * The Large Object Space has been swept.
140   */
141  static final int AFTER_COLLECTION  = SEMISPACE_COPIED + 1;
142
143  static int gcspyEvent_ = BEFORE_COLLECTION;
144
145  // The specific drivers for this collector
146  static LinearSpaceDriver ss0Driver;
147  static LinearSpaceDriver ss1Driver;
148  static ImmortalSpaceDriver immortalDriver;
149  static TreadmillDriver losNurseryDriver;
150  static TreadmillDriver losDriver;
151  static TreadmillDriver plosNurseryDriver;
152  static TreadmillDriver plosDriver;
153
154  private static final boolean DEBUG = false;
155
156
157  static {
158    GCspy.createOptions();
159  }
160
161  /**
162   * Start the server and wait if necessary.
163   * This method has the following responsibilities.
164   * <ol>
165   * <li> Create and initialise the GCspy server by calling.
166   *      <pre>server = ServerInterpreter.init(name, portNumber, verbose);</pre>
167   * <li> Add each event to the ServerInterpreter
168   *      <pre>server.addEvent(eventID, eventName);</pre>
169   * <li> Set some general information about the server (e.g. name of the collector, build, etc).
170   *      <pre>server.setGeneralInfo(info); </pre>
171   * <li> Create new drivers for each component to be visualised.
172   *      <pre>myDriver = new MyDriver(server, args...);</pre>
173   *      Drivers extend AbstractDriver and register their spce with the
174   *      ServerInterpreter. In addition to the server, drivers will take as
175   *      arguments the name of the space, the MMTk space, the tilesize, and
176   *      whether this space is to be the main space in the visualiser.
177   * </ol>
178   *
179   * WARNING: allocates memory.
180   * @param wait Whether to wait
181   * @param port The port to talk to the GCspy client (e.g. visualiser)
182   */
183  @Override
184  @Interruptible
185  public final void startGCspyServer(int port, boolean wait) {
186    GCspy.server.init("SemiSpaceServerInterpreter", port, true/*verbose*/);
187    if (DEBUG) Log.writeln("SSGCspy: ServerInterpreter initialised");
188
189    GCspy.server.addEvent(BEFORE_COLLECTION, "Before collection");
190    GCspy.server.addEvent(SEMISPACE_COPIED, "Semispace copied; LOS traced");
191    GCspy.server.addEvent(AFTER_COLLECTION, "After collection; LOS swept");
192    GCspy.server.setGeneralInfo(
193        "SSGCspy\n\nRichard Jones, October 2006\\http://www.cs.kent.ac.uk/~rej/");
194    if (DEBUG) Log.writeln("SSGCspy: events added to ServerInterpreter");
195
196    // Initialise each driver
197    ss0Driver      = newLinearSpaceDriver("Semispace 0 Space", copySpace0, true);
198    ss1Driver      = newLinearSpaceDriver("Semispace 1 Space", copySpace1, false);
199    immortalDriver = new ImmortalSpaceDriver(
200                         GCspy.server,  "Immortal Space", immortalSpace,
201                         Options.gcspyTileSize.getValue(), false);
202    losNurseryDriver  = newTreadmillDriver("LOS Nursery", loSpace);
203    losDriver         = newTreadmillDriver("LOS", loSpace);
204
205    if (DEBUG) Log.write("SemiServerInterpreter initialised\n");
206
207    // Register drivers to allow immortal space to notify direct references
208    immortalDriver.registerDriversForReferenceNotification(
209      new AbstractDriver[] {ss0Driver, ss1Driver, immortalDriver,
210                            losNurseryDriver, losDriver,
211                            plosNurseryDriver, plosDriver});
212    if (DEBUG) Log.writeln("SSGCspy: registered drivers");
213
214    gcspyEvent_ = BEFORE_COLLECTION;
215
216    // Start the server
217    GCspy.server.startServer(wait);
218  }
219
220  /**
221   * Create a new LinearSpaceDriver
222   * TODO is this the best name or should we call it LargeObjectSpaceDriver?
223   * @param name Name of the space
224   * @param space The space
225   * @param mainSpace whether the space will be the main space
226   * @return A new GCspy driver for this space
227   */
228  @Interruptible
229  private LinearSpaceDriver newLinearSpaceDriver(String name, CopySpace space, boolean mainSpace) {
230    // TODO What if tileSize is too small (i.e. too many tiles for GCspy buffer)
231    // TODO stop the GCspy spaces in the visualiser from fluctuating in size
232    // so much as we resize them.
233    return new LinearSpaceDriver(GCspy.server, name, space,
234            Options.gcspyTileSize.getValue(), mainSpace);
235  }
236
237  /**
238   * Create a new TreadmillDriver
239   * TODO is this the best name or should we call it LargeObjectSpaceDriver?
240   * @param name Name of the space
241   * @param space The space
242   * @return A new GCspy driver for this space
243   */
244  @Interruptible
245  private TreadmillDriver newTreadmillDriver(String name, LargeObjectSpace space) {
246    return new TreadmillDriver(GCspy.server, name, space,
247            Options.gcspyTileSize.getValue(), MAX_NON_LOS_COPY_BYTES, false);
248  }
249
250  /****************************************************************************
251   *
252   * Collection
253   */
254
255  /**
256   * {@inheritDoc}
257   */
258  @Override
259  @Inline
260  public void collectionPhase(short phaseId) {
261    if (DEBUG) {
262      Log.write("--Phase Plan.");
263      Log.writeln(Phase.getName(phaseId));
264    }
265
266    if (phaseId == SSGCspy.PREPARE) {
267      super.collectionPhase(phaseId);
268      gcspySpace.prepare();
269      return;
270    }
271
272    if (phaseId == SSGCspy.RELEASE) {
273      super.collectionPhase(phaseId);
274      gcspySpace.release();
275      //if (primary)
276       gcspyGatherData(SSGCspy.AFTER_COLLECTION);
277      return;
278    }
279
280    super.collectionPhase(phaseId);
281  }
282
283  /**
284   * Gather data for GCspy for the semispaces, the immortal space and the large
285   * object space.
286   * <p>
287   * This method sweeps the semispace under consideration to gather data.
288   * Alternatively and more efficiently, 'used space' can obviously be
289   * discovered in constant time simply by comparing the start and the end
290   * addresses of the semispace. However, per-object information can only be
291   * gathered by sweeping through the space and we do this here for tutorial
292   * purposes.
293   *
294   * @param event
295   *          The event, either BEFORE_COLLECTION, SEMISPACE_COPIED or
296   *          AFTER_COLLECTION
297   */
298  private void gcspyGatherData(int event) {
299    if (DEBUG) {
300      Log.writeln("SSGCspy.gcspyGatherData, event=", event);
301      Log.writeln("SSGCspy.gcspyGatherData, port=", GCspy.getGCspyPort());
302    }
303
304    // If port = 0 there can be no GCspy client connected
305    if (GCspy.getGCspyPort() == 0)
306      return;
307
308    // This is a safepoint for the server, i.e. it is a point at which
309    // the server can pause.
310    // The Mutator is called after the Collector so the Mutator must set the safepoint
311    if (DEBUG) Log.writeln("SSGCspy safepoint");
312    GCspy.server.serverSafepoint(event);
313  }
314
315  /****************************************************************************
316   *
317   * Accounting
318   */
319
320  /**
321   * Return the number of pages reserved for use given the pending
322   * allocation.  This is <i>exclusive of</i> space reserved for
323   * copying.
324   */
325  @Override
326  public final int getPagesUsed() {
327    return super.getPagesUsed() + gcspySpace.reservedPages();
328  }
329
330
331  /**
332   * Report information on the semispaces
333   */
334  static void reportSpaces() {
335    Log.write("\n  Low semispace:  ");
336    Log.write(SSGCspy.copySpace0.getStart());
337    Log.write(" - ");
338    Log.write(SSGCspy.copySpace0.getStart()
339        .plus(SSGCspy.copySpace0.getExtent()));
340    Log.write("\n  High semispace: ");
341    Log.write(SSGCspy.copySpace1.getStart());
342    Log.write(" - ");
343    Log.write(SSGCspy.copySpace1.getStart()
344        .plus(SSGCspy.copySpace1.getExtent()));
345    Log.flush();
346  }
347
348  @Override
349  @Interruptible
350  protected void registerSpecializedMethods() {
351    super.registerSpecializedMethods();
352    TransitiveClosure.registerSpecializedScan(SCAN_SS, SSGCspyTraceLocal.class);
353  }
354}