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