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.utility.gcspy.drivers;
014
015import org.mmtk.policy.Space;
016import org.mmtk.utility.Log;
017import org.mmtk.utility.gcspy.GCspy;
018import org.mmtk.utility.gcspy.Subspace;
019import org.mmtk.vm.gcspy.ServerSpace;
020import org.mmtk.vm.gcspy.ServerInterpreter;
021import org.mmtk.vm.gcspy.Stream;
022import org.mmtk.vm.VM;
023
024import org.vmmagic.unboxed.*;
025import org.vmmagic.pragma.*;
026
027/**
028 * Abstract GCspy driver for MMTk collectors.<p>
029 *
030 * This class implements for the MMTk a base driver for a GCspy space.
031 * All drivers for GCspy spaces should inherit from this class.
032 */
033@Uninterruptible
034public abstract class AbstractDriver {
035
036  /****************************************************************************
037   *
038   * Class variables
039   */
040
041  // Controls used for tile presentation
042
043  /** The tile is used */
044  protected static final byte CONTROL_USED            =  1;
045  /** The tile is a background tile */
046  protected static final byte CONTROL_BACKGROUND      =  2;
047  /** The tile is unused */
048  protected static final byte CONTROL_UNUSED          =  4;
049  /** The tile is a separator */
050  protected static final byte CONTROL_SEPARATOR       =  8;
051  /** The tile is a link */
052  protected static final byte CONTROL_LINK            = 16;
053
054
055  private static final int MAX_STREAMS = 64;    // Max number of streams
056
057  private static final boolean DEBUG = false;
058  protected String myClass;                     // used in debugging messages
059
060
061  /****************************************************************************
062   *
063   * Instance variables
064   */
065
066  /** The owning GCspy server */
067  protected final ServerInterpreter server;
068  /** The name of the GCspy space driver */
069  protected final String name;
070  /** The GCspy space abstraction */
071  protected final ServerSpace serverSpace;
072  /** The MMTK space */
073  protected final Space mmtkSpace;
074  /** The GCspy space's block size */
075  protected int blockSize;
076  /** The maximum number of tiles in this GCspy space */
077  protected int maxTileNum;
078  /** This space's streams  */
079  protected Stream[] streams;
080  /**  control values for tiles in this space */
081  protected byte[] control;
082  /** Has this space changed? */
083  protected boolean changed = true;
084
085
086  /**
087   * Create a new driver for this collector.
088   *
089   * @param server The ServerInterpreter that owns this GCspy space.
090   * @param name The name of this driver.
091   * @param mmtkSpace The MMTk space represented by this driver.
092   * @param blockSize The tile size.
093   * @param mainSpace Is this the main space?
094   */
095  public AbstractDriver(ServerInterpreter server,
096                        String name,
097                        Space mmtkSpace,
098                        int blockSize,
099                        boolean mainSpace) {
100    this.server = server;
101    this.name = name;
102    this.mmtkSpace = mmtkSpace;
103    this.blockSize = blockSize;
104    myClass = getClass().getName();
105    maxTileNum = countTileNum(mmtkSpace.getExtent(), blockSize);
106    control = (byte[])GCspy.util.createDataArray(new byte[0], maxTileNum);
107    // to avoid allocation during GC we preallocate the streams array
108    streams = new Stream[MAX_STREAMS];
109    serverSpace = createServerSpace(server, name, maxTileNum, mainSpace);
110  }
111
112  /**
113   * Creates a subspace for this space.
114   * Subspace provide useful facilities for contiguous spaces, even if
115   * a space contains only one.
116   * @param mmtkSpace The MMTk space
117   * @return the created subspace
118   */
119  @Interruptible
120  protected Subspace createSubspace(Space mmtkSpace) {
121    Address start = mmtkSpace.getStart();
122    return new Subspace(start, start, 0, blockSize, 0);
123  }
124
125  /**
126   * Create a new GCspy ServerSpace and add it to the ServerInterpreter.
127   * @param server the GCspy ServerInterpreter.
128   * @param spaceName The name of this driver.
129   * @param maxTileNum the maximum number of tiles in this space.
130   * @param mainSpace Is this the main space?
131   * @return the created server space
132   */
133  @Interruptible
134  protected ServerSpace createServerSpace(ServerInterpreter server,
135                  String spaceName,
136                  int maxTileNum,
137                  boolean mainSpace) {
138    // Set the block label
139    String tmp = "Block Size: " + ((blockSize < 1024) ?
140                     blockSize + " bytes\n" :
141                     (blockSize / 1024) + " Kbytes\n");
142
143    // Create a single GCspy Space
144    return VM.newGCspyServerSpace(server,           // the server
145                                  spaceName,        // space name
146                                  getDriverName(),  // driver (space) name
147                                  "Block ",         // space title
148                                  tmp,              // block info
149                                  maxTileNum,       // number of tiles
150                                  "UNUSED",         // the label for unused blocks
151                                  mainSpace);       // main space
152  }
153
154  /**
155   * Get the name of this driver type.
156   * @return The name of this driver.
157   */
158  protected abstract String getDriverName();
159
160  /**
161   * Get the maximum number of tiles in this space.
162   * @return the maximum number of tiles in the space.
163   */
164  public int getMaxTileNum() {
165    return maxTileNum;
166  }
167
168  /**
169   * The GCspy space managed by this driver.
170   * @return the GCspy server space.
171   */
172  public ServerSpace getServerSpace() {
173    return serverSpace;
174  }
175
176  /**
177   * Add a stream to the driver. This also sets the stream's id
178   * (unique for this space).
179   * @param stream The stream
180   * @exception IndexOutOfBoundsException if more than MAX_STREAMS are added
181   */
182  @Interruptible
183  public void addStream(Stream stream) {
184    int id = 0;
185    while (id < MAX_STREAMS) {
186      if (streams[id] == null) {
187        streams[id] = stream;
188        if (DEBUG) {
189          Log.write("Adding stream with id=");
190          Log.writeln(id);
191        }
192        Address stream_ = serverSpace.addStream(id);
193        stream.setStream(id, stream_);
194        return;
195      }
196      id++;
197    }
198    throw new IndexOutOfBoundsException("Too many streams added to driver " + name);
199  }
200
201  /**
202   * Count number of tiles in an address range.
203   * @param start The start of the range.
204   * @param end The end of the range.
205   * @param tileSize The size of each tile.
206   * @return The number of tiles in this range.
207   */
208  protected int countTileNum(Address start, Address end, int tileSize) {
209    if (end.LE(start)) return 0;
210    int diff = end.diff(start).toInt();
211    return countTileNum(diff, tileSize);
212  }
213
214  /**
215   * Count number of tiles in an address range.
216   * @param extent The extent of the range.
217   * @param tileSize The size of each tile.
218   * @return The number of tiles in this range.
219   */
220  protected int countTileNum(Extent extent, int tileSize) {
221    int diff = extent.toInt();
222    return countTileNum(diff, tileSize);
223  }
224
225  private int countTileNum(int diff, int tileSize) {
226    int tiles = diff / tileSize;
227    if ((diff % tileSize) != 0)
228      ++tiles;
229    return tiles;
230  }
231
232  /**
233   * Indicate the limits of a space.
234   *
235   * @param start the Address of the start of the space.
236   * @param end the Address of the end of the space.
237   */
238  public void setRange(Address start, Address end) {}
239
240  /**
241   * Indicate the limits of a space.
242   *
243   * @param start the Address of the start of the space.
244   * @param extent the extent of the space.
245   */
246  public void setRange(Address start, Extent extent) {
247    setRange(start, start.plus(extent));
248  }
249
250  /**
251   * Setup the tile names in a subspace. Tile names are typically
252   * address ranges but may be anything (e.g. a size class if the
253   * space is a segregated free-list manager, or a class name if the
254   * space represents the class instances loaded).
255   *
256   * @param subspace the Subspace
257   * @param numTiles the number of tiles to name
258   */
259  protected void setTilenames(Subspace subspace, int numTiles) {
260    Address start = subspace.getStart();
261    int first = subspace.getFirstIndex();
262    int bs = subspace.getBlockSize();
263
264    for (int i = 0; i < numTiles; ++i) {
265      if (subspace.indexInRange(i))
266        serverSpace.setTilename(i, start.plus((i - first) * bs),
267                                start.plus((i + 1 - first) * bs));
268    }
269  }
270
271  /**
272   * The "typical" maximum number of objects in each tile.
273   * @param blockSize The size of a tile
274   * @return The maximum number of objects in a tile
275   */
276  public int maxObjectsPerBlock(int blockSize) {
277    // Maybe a misuse of ServerInterpreter but it's a convenient
278    // VM-dependent class
279    return blockSize / GCspy.server.computeHeaderSize();
280  }
281
282  /**
283   * @param event The current event
284   * @return whether the server is connected to a GCspy client
285   */
286  public boolean isConnected(int event) {
287    return server.isConnected(event);
288  }
289
290  /**
291   * Reset the statistics for a space.
292   * In this base driver, we simply note that the data has changed.
293   */
294  protected void resetData() {
295    changed = true;
296  }
297
298  /**
299   * Scan an object found at a location.
300   * Collectors typically call this method to update GCspy statistics.
301   * The driver may or may not accumulate values found, depending on
302   * the value of total.
303   * @param obj the reference to the object found
304   * @param total Whether to total the statistics
305   */
306  public void scan(ObjectReference obj, boolean total) {}
307
308  /**
309   * Scan an object found at a location.
310   * Collectors typically call this method to update GCspy statistics
311   * The driver will accumulate values found.
312   * @param obj the reference to the object found
313   */
314  public void scan(ObjectReference obj) {
315    scan(obj, true);
316  }
317
318  /**
319   * Scan an object found at a location.
320   * Collectors typically call this method to update GCspy statistics.
321   * The driver may or may not accumulate values found, depending on
322   * the value of total.
323   * @param obj the reference to the object found
324   * @param total Whether to total the statistics
325   */
326  public void scan(Address obj, boolean total) {}
327
328  /**
329   * Scan an object found at a location.
330   * Collectors typically call this method to update GCspy statistics
331   * The driver will accumulate values found.
332   * @param obj the reference to the object found
333   */
334  public void scan(Address obj) {}
335
336  /**
337   * Handle a direct reference from the immortal space.<p>
338   * This is an empty implementation. Subclasses may override this method
339   * to increment their <code>refFromImmortal</code> Stream.
340   *
341   * @param addr The Address
342   * @return {@code true} if the given Address is in this subspace. Always {@code false} here.
343   */
344  public boolean handleReferenceFromImmortalSpace(Address addr) {
345    return false;
346  }
347
348  /**
349   * Set space info.
350   * This simply reports the size of the current space.
351   * Drivers that want to send something more complex than
352   *  "Current Size: size\n"
353   * must override this method.
354   *
355   * @param size the size of the space
356   */
357  protected void setSpaceInfo(Offset size) {
358    //    - sprintf(tmp, "Current Size: %s\n", gcspy_formatSize(size));
359    Address tmp = GCspy.util.formatSize("Current Size: %s\n", 128, size.toInt());
360    serverSpace.spaceInfo(tmp);
361    GCspy.util.free(tmp);
362  }
363
364
365  /****************************************************************************
366   *
367   * Control values
368   */
369
370  /**
371   * Is a tile used?
372   * @param val the control value.
373   * @return {@code true} if the tile is used
374   */
375  protected static boolean controlIsUsed(byte val) {
376    return (val & CONTROL_USED) != 0;
377  }
378
379  /**
380   * Is a tile a background pseudo-tile?
381   * @param val the control value.
382   * @return {@code true} if the tile is a background tile
383   */
384  protected static boolean controlIsBackground(byte val) {
385    return (val & CONTROL_BACKGROUND) != 0;
386  }
387
388  /**
389   * Is a tile unused?
390   * @param val the control value.
391   * @return {@code true} if the tile is unused
392   */
393  protected static boolean controlIsUnused(byte val) {
394    return (val & CONTROL_UNUSED) != 0;
395  }
396
397  /**
398   * Is this a separator?
399   * @param val the control value.
400   * @return {@code true} if this is a separator
401   */
402  protected static boolean controlIsSeparator(byte val) {
403    return (val & CONTROL_SEPARATOR) != 0;
404  }
405
406  /**
407   * Initialise the value of a control.
408   * @param index The index of the tile.
409   * @param value The new value of the control
410   */
411  protected void initControl(int index, byte value) {
412    control[index] = value;
413  }
414
415  /**
416   * Add a control to the tile
417   * @param index The index of the tile.
418   * @param value The control to add.
419   */
420  protected void addControl(int index, byte value) {
421    control[index] |= value;
422  }
423
424  /** Set the control
425   * @param index the index of the tile
426   * @param value The value to set
427   */
428  protected void setControl(int index, byte value) {
429    control[index] &= value;
430  }
431
432  /**
433   * Get the controls for a tile.
434   * @param index The index of the tile.
435   * @return The value of the controls
436   */
437  public byte getControl(int index) {
438    return control[index];
439  }
440
441  /**
442   * Initialise control values in all tiles
443   */
444  protected void initControls() {
445    for (int index = 0; index < control.length; ++index) {
446      initControl(index, CONTROL_USED);
447    }
448  }
449
450  /**
451   * Set the control value in each tile in a region.
452   * @param tag The control tag.
453   * @param start The start index of the region.
454   * @param len The number of tiles in the region.
455   */
456  protected void controlValues(byte tag, int start, int len) {
457    if (DEBUG) {
458      Log.write("AbstractDriver.controlValues for space ");
459      Log.write(name);
460      Log.write(", control length=", control.length);
461      Log.write(" writing controls from ", start);
462      Log.writeln(" to ", start + len);
463    }
464    changed = true;
465    for (int i = start; i < (start + len); ++i) {
466      // Cannot be both USED and UNUSED or BACKGROUND
467      if (controlIsBackground(tag) || controlIsUnused(tag))
468        setControl(i, (byte)~CONTROL_USED);
469      else if (controlIsUsed(tag))
470        setControl(i, (byte)~CONTROL_UNUSED);
471      addControl(i, tag);
472    }
473  }
474
475  /**
476   * Transmit the streams for this space. A driver will typically
477   * <ol>
478   * <li> Determine whether a GCspy client is connected and interested in
479   *      this event, e.g.
480   *      <pre>server.isConnected(event)</pre>
481   * <li> Setup the summaries for each stream, e.g.
482   *      <pre>stream.setSummary(values...);</pre>
483   * <li> Setup the control information for each tile. e.g.
484   *      <pre>controlValues(CONTROL_USED, start, numBlocks);</pre>
485   *      <pre>controlValues(CONTROL_UNUSED, end, remainingBlocks);</pre>
486   * <li> Set up the space information, e.g.
487   *      <pre>setSpace(info);</pre>
488   * <li> Send the data for all streams, e.g.
489   *      <pre>send(event, numTiles);</pre>
490   *      Note that AbstractDriver.send takes care of sending the information
491   *      for all streams (including control data).
492   * </ol>
493   *
494   * @param event The event
495   */
496  public abstract void transmit(int event);
497
498  /**
499   * Send all the streams for this space if it has changed.
500   * Assume that the data has been gathered and that summary info
501   * and control values have been set before this is called.
502   *
503   * @param event the event
504   * @param numTiles the number of blocks in this space
505   */
506  protected void send(int event, int numTiles) {
507    if (changed) {
508      serverSpace.startCommunication();
509      for (int i = 0; i < MAX_STREAMS; i++)
510        if (streams[i] != null)
511          streams[i].send(event, numTiles);
512      serverSpace.sendControls(this, numTiles);
513      serverSpace.endCommunication();
514    }
515  }
516}