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