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 static org.mmtk.utility.gcspy.StreamConstants.PAINT_STYLE_ZERO;
016import static org.mmtk.utility.gcspy.StreamConstants.PRESENTATION_PERCENT;
017import static org.mmtk.utility.gcspy.StreamConstants.PRESENTATION_PLUS;
018
019import org.mmtk.policy.Space;
020import org.mmtk.utility.Log;
021import org.mmtk.utility.gcspy.Color;
022import org.mmtk.utility.gcspy.LinearScan;
023import org.mmtk.utility.gcspy.Subspace;
024import org.mmtk.vm.VM;
025import org.mmtk.vm.gcspy.IntStream;
026import org.mmtk.vm.gcspy.ServerInterpreter;
027import org.mmtk.vm.gcspy.ShortStream;
028import org.vmmagic.pragma.Interruptible;
029import org.vmmagic.pragma.Uninterruptible;
030import org.vmmagic.unboxed.Address;
031import org.vmmagic.unboxed.ObjectReference;
032import org.vmmagic.unboxed.Offset;
033
034/**
035 * GCspy driver for the MMTk ContigousSpace.<p>
036 *
037 * This class implements a simple driver for contiguous MMTk spaces
038 * such as CopySpace and ImmortalSpace.
039 */
040@Uninterruptible public class LinearSpaceDriver extends AbstractDriver {
041
042  // The GCspy streams
043  protected IntStream   scalarUsedSpaceStream;
044  protected IntStream   arrayUsedSpaceStream;
045  protected ShortStream scalarObjectsStream;
046  protected ShortStream arrayObjectsStream;
047  protected ShortStream arrayPrimitiveStream;
048  protected ShortStream rootsStream;
049  protected ShortStream refFromImmortalStream;
050
051  protected Subspace subspace;               // A subspace for all of this space
052  protected int allTileNum;                  // total number of tiles
053
054  // Overall statistics
055  protected int totalScalarObjects   = 0;    // total number of objects allocated
056  protected int totalArrayObjects    = 0;
057  protected int totalPrimitives      = 0;
058  protected int totalScalarUsedSpace = 0;    // total space used
059  protected int totalArrayUsedSpace  = 0;
060  protected int totalRoots           = 0;
061  protected int totalRefFromImmortal = 0;
062
063  private final LinearScan scanner;          // A scanner to trace objects
064
065  // Debugging
066  protected Address lastAddress = Address.zero();
067  protected int lastSize = 0;
068  private static final boolean DEBUG = false;
069
070
071  /**
072   * Create a new driver for a contiguous MMTk space.
073   *
074   * @param server The GCspy ServerInterpreter
075   * @param spaceName The name of this GCspy space
076   * @param mmtkSpace The MMTk space
077   * @param blockSize The tile size
078   * @param mainSpace Is this the main space?
079   */
080  public LinearSpaceDriver(ServerInterpreter server,
081                           String spaceName,
082                           Space mmtkSpace,
083                           int blockSize,
084                           boolean mainSpace) {
085
086    super(server, spaceName, mmtkSpace, blockSize, mainSpace);
087
088    if (DEBUG) {
089      Log.write("LinearSpaceDriver for "); Log.write(spaceName);
090      Log.write(", blocksize="); Log.write(blockSize);
091      Log.write(", start="); Log.write(mmtkSpace.getStart());
092      Log.write(", extent="); Log.write(mmtkSpace.getExtent());
093      Log.write(", maxTileNum="); Log.writeln(maxTileNum);
094    }
095
096    // Initialise a subspace and 4 Streams
097    subspace = createSubspace(mmtkSpace);
098    allTileNum = 0;
099    scalarUsedSpaceStream = createScalarUsedSpaceStream();
100    arrayUsedSpaceStream  = createArrayUsedSpaceStream();
101    scalarObjectsStream   = createScalarObjectsStream();
102    arrayPrimitiveStream  = createArrayPrimitiveStream();
103    arrayObjectsStream    = createArrayObjectsStream();
104    rootsStream           = createRootsStream();
105    refFromImmortalStream = createRefFromImmortalStream();
106    serverSpace.resize(0); // the collector must call resize() before gathering data
107
108    // Initialise the statistics
109    resetData();
110    scanner = new LinearScan(this);
111  }
112
113  @Override
114  protected String getDriverName() {
115    return "MMTk LinearSpaceDriver";
116  }
117
118  /*
119   * Private creator methods to create the Streams.
120   */
121
122
123  @Interruptible
124  private IntStream createScalarUsedSpaceStream() {
125    return VM.newGCspyIntStream(
126                     this,
127                     "Scalar Used Space stream",            // stream name
128                     0,                                     // min. data value
129                     blockSize,                             // max. data value
130                     0,                                     // zero value
131                     0,                                     // default value
132                    "Scalars and primitive arrays: ",       // value prefix
133                    " bytes",                               // value suffix
134                     PRESENTATION_PERCENT,  // presentation style
135                     PAINT_STYLE_ZERO,      // paint style
136                     0,                                     // index of max stream (only needed if the presentation is *_VAR)
137                     Color.Red,                             // tile colour
138                     true);                                 // summary enabled
139  }
140
141  @Interruptible
142  private IntStream createArrayUsedSpaceStream() {
143    return VM.newGCspyIntStream(
144                     this,
145                     "Array Used Space stream",
146                     0,
147                     blockSize,
148                     0,
149                     0,
150                    "Reference arrays: ",
151                    " bytes",
152                     PRESENTATION_PERCENT,
153                     PAINT_STYLE_ZERO,
154                     0,
155                     Color.Blue,
156                     true);
157  }
158
159  @Interruptible
160  private ShortStream createScalarObjectsStream() {
161    return VM.newGCspyShortStream(
162                     this,
163                     "Scalar Objects stream",
164                     (short)0,
165                     // Say, max value = 50% of max possible
166                     (short)(maxObjectsPerBlock(blockSize) / 2),
167                     (short)0,
168                     (short)0,
169                     "Scalars: ",
170                     " objects",
171                     PRESENTATION_PLUS,
172                     PAINT_STYLE_ZERO,
173                     0,
174                     Color.Green,
175                     true);
176  }
177
178  @Interruptible
179  private ShortStream createArrayPrimitiveStream() {
180    return VM.newGCspyShortStream(
181                     this,
182                     "Array Primitive stream",
183                     (short)0,
184                     // Say, typical primitive array size = 4 * typical scalar size?
185                     (short)(maxObjectsPerBlock(blockSize) / 8),
186                     (short)0,
187                     (short)0,
188                     "Primitive arrays: ",
189                     " objects",
190                     PRESENTATION_PLUS,
191                     PAINT_STYLE_ZERO,
192                     0,
193                     Color.Yellow,
194                     true);
195  }
196
197  @Interruptible
198  private ShortStream createArrayObjectsStream() {
199    return VM.newGCspyShortStream(
200                     this,
201                     "Array Objects stream",
202                     (short)0,
203                     // Say, typical ref array size = 4 * typical scalar size?
204                     (short)(maxObjectsPerBlock(blockSize) / 8),
205                     (short)0,
206                     (short)0,
207                     "Reference arrays: ",
208                     " objects",
209                     PRESENTATION_PLUS,
210                     PAINT_STYLE_ZERO,
211                     0,
212                     Color.Cyan,
213                     true);
214  }
215
216  @Interruptible
217  private ShortStream createRootsStream() {
218    return VM.newGCspyShortStream(
219                     this,
220                     "Roots stream",
221                     (short)0,
222                     // Say, typical size = 4 * typical scalar size?
223                     (short)(maxObjectsPerBlock(blockSize) / 8),
224                     (short)0,
225                     (short)0,
226                     "Roots: ",
227                     " objects",
228                     PRESENTATION_PLUS,
229                     PAINT_STYLE_ZERO,
230                     0,
231                     Color.Blue,
232                     true);
233  }
234
235  @Interruptible
236  private ShortStream createRefFromImmortalStream() {
237    return VM.newGCspyShortStream(
238                     this,
239                     "References from immortal stream",
240                     (short)0,
241                     // Say, typical size = 4 * typical scalar size?
242                     (short)(maxObjectsPerBlock(blockSize) / 8),
243                     (short)0,
244                     (short)0,
245                     "References from immortal space: ",
246                     " references",
247                     PRESENTATION_PLUS,
248                     PAINT_STYLE_ZERO,
249                     0,
250                     Color.Blue,
251                     true);
252  }
253
254  /**
255   * Reset the statistics for all the streams, including totals used for summaries
256   */
257  @Override
258  public void resetData() {
259    super.resetData();
260
261    // Reset all the streams
262    scalarUsedSpaceStream.resetData();
263    arrayUsedSpaceStream.resetData();
264    scalarObjectsStream.resetData();
265    arrayObjectsStream.resetData();
266    arrayPrimitiveStream.resetData();
267    refFromImmortalStream.resetData();
268
269    // Reset the summary counts
270    totalScalarObjects   = 0;
271    totalArrayObjects    = 0;
272    totalPrimitives      = 0;
273    totalScalarUsedSpace = 0;
274    totalArrayUsedSpace  = 0;
275    totalRefFromImmortal = 0;
276  }
277
278  /**
279   * BumpPointer.linearScan needs a LinearScan object, which we provide here.
280   * @return the scanner for this driver
281   */
282   public LinearScan getScanner() {
283     return scanner;
284   }
285
286   /**
287    * Set the current address range of a contiguous space
288    * @param start the start of the contiguous space
289    * @param end the end of the contiguous space
290    */
291  @Override
292  public void setRange(Address start, Address end) {
293    int current = subspace.getBlockNum();
294    int required = countTileNum(start, end, subspace.getBlockSize());
295
296    // Reset the subspace
297    if (required != current)
298      subspace.reset(start, end, 0, required);
299
300    if (DEBUG) {
301      Log.write("\nContiguousSpaceDriver.setRange for contiguous space: ");
302      Log.write(subspace.getFirstIndex()); Log.write("-", subspace.getBlockNum());
303      Log.write(" (", start); Log.write("-", end); Log.write(")");
304    }
305
306    // Reset the driver
307    // Note release() only resets a CopySpace's  cursor (and optionally zeroes
308    // or mprotects the pages); it doesn't make the pages available to other
309    // spaces. If pages were to be released, change the test here to
310    //     if (allTileNum != required) {
311    if (allTileNum < required) {
312      if (DEBUG) {
313        Log.write(", resize from ", allTileNum);
314        Log.write(" to ", required);
315      }
316      allTileNum = required;
317      serverSpace.resize(allTileNum);
318      setTilenames(subspace, allTileNum);
319    }
320    if (DEBUG) Log.writeln();
321  }
322
323
324  /**
325   * Update the tile statistics
326   * @param obj The current object
327   */
328  @Override
329  public void  scan(ObjectReference obj) {
330    scan(obj, true);
331  }
332
333  /**
334   * Update the tile statistics
335   * @param obj The current object
336   * @param total Whether to accumulate the values
337   */
338  @Override
339  public void scan(ObjectReference obj, boolean total) {
340    boolean isArray = VM.objectModel.isArray(obj);
341    int length = VM.objectModel.getCurrentSize(obj);
342    Address addr = obj.toAddress();
343
344    if (VM.VERIFY_ASSERTIONS) {
345      if (addr.LT(lastAddress.plus(lastSize))) {
346        Log.write("\nContiguousSpaceDriver finds addresses going backwards: ");
347        Log.write("last="); Log.write(lastAddress);
348        Log.write(" last size="); Log.write(lastSize);
349        Log.writeln(" current=", addr);
350      }
351      lastAddress = addr;
352      lastSize = length;
353    }
354
355    // Update the stats
356    if (subspace.addressInRange(addr)) {
357      int index = subspace.getIndex(addr);
358      int remainder = subspace.spaceRemaining(addr);
359      if (isArray) {
360        arrayObjectsStream.increment(index, (short)1);
361        arrayUsedSpaceStream.distribute(index, remainder, blockSize, length);
362        if (total) {
363          totalArrayObjects++;
364          totalArrayUsedSpace += length;
365        }
366      } else {
367        if (!this.scanCheckPrimitiveArray(obj, index, total, length)) {
368          // real object
369          scalarObjectsStream.increment(index, (short)1);
370          if (total) {
371            totalScalarObjects++;
372            totalScalarUsedSpace += length;
373          }
374        }
375        scalarUsedSpaceStream.distribute(index, remainder, blockSize, length);
376      }
377    }
378  }
379
380  /**
381   * Check if this Object is an array of primitives.<br>
382   * Part of the public scan() method.
383   *
384   * @param obj The Object to check
385   * @param index Index of the tile
386   * @param total Increment summary
387   * @param length Current size of the Object, will be added to array space summary.
388   * @return {@code true} if this Object is an array of primitives.
389   */
390  protected boolean scanCheckPrimitiveArray(ObjectReference obj, int index, boolean total, int length) {
391    if (VM.objectModel.isPrimitiveArray(obj)) {
392      arrayPrimitiveStream.increment(index, (short)1);
393      if (total) {
394        totalPrimitives++;
395        totalScalarUsedSpace += length;
396      }
397      return true;
398    } else {
399      return false;
400    }
401  }
402
403  /**
404   * Transmit the data if this event is of interest to the client.<p>
405   * Implemented using the algorithm pattern, subclasses can override parts of it.
406   * @param event The event, defined in the Plan
407   */
408  @Override
409  public void transmit(int event) {
410    if (!server.isConnected(event))
411      return;
412
413    if (DEBUG) {
414      Log.write("CONNECTED\n");
415      Log.write(myClass);
416      Log.write(".send: numTiles=", allTileNum);
417      //Log.write("LinearSpaceDriver.transmit: numTiles=", allTileNum);
418      Log.writeln(", control.length=", control.length);
419      Log.flush();
420    }
421
422    // Setup the summaries
423    setupSummaries();
424
425    // Setup the control info
426    setupControlInfo();
427
428    // Setup the space info
429    Offset size = subspace.getEnd().diff(subspace.getStart());
430    setSpaceInfo(size);
431
432    // Send the all streams
433    send(event, allTileNum);
434
435    // Debugging
436    if (VM.VERIFY_ASSERTIONS) {
437      lastAddress = Address.zero();
438      lastSize = 0;
439    }
440  }
441
442  /**
443   * Setup summaries part of the <code>transmit</code> method.<p>
444   * Override this method to setup summaries of additional streams in subclasses.
445 */
446  protected void setupSummaries() {
447    scalarUsedSpaceStream.setSummary(totalScalarUsedSpace,
448                                     subspace.getEnd().diff(subspace.getStart()).toInt());
449    arrayUsedSpaceStream.setSummary(totalArrayUsedSpace,
450                                    subspace.getEnd().diff(subspace.getStart()).toInt());
451    scalarObjectsStream.setSummary(totalScalarObjects);
452    arrayObjectsStream.setSummary(totalArrayObjects);
453    arrayPrimitiveStream.setSummary(totalPrimitives);
454    rootsStream.setSummary(totalRoots);
455    refFromImmortalStream.setSummary(totalRefFromImmortal);
456  }
457
458  /**
459   * Setup control info part of the <code>transmit</code> method.<p>
460   * Override this method to change the controls for your own driver subclass.
461 */
462  protected void setupControlInfo() {
463    int numBlocks = subspace.getBlockNum();
464    controlValues(CONTROL_USED, subspace.getFirstIndex(), numBlocks);
465    if (DEBUG) {
466      Log.write("LinearSpaceDriver.transmitSetupControlInfo: allTileNum=", allTileNum);
467      Log.writeln(", numBlocks=", numBlocks);
468    }
469    if (numBlocks < allTileNum)
470      controlValues(CONTROL_UNUSED,
471                    subspace.getFirstIndex() + numBlocks,
472                    allTileNum - numBlocks);
473  }
474
475  /**
476   * Handle a root address
477   *
478   * @param addr Root Address
479   * @return {@code true} if the given Address is in this subspace.
480   */
481  public boolean handleRoot(Address addr) {
482    if (subspace.addressInRange(addr)) {
483      // increment tile
484      int index = subspace.getIndex(addr);
485      rootsStream.increment(index, (short)1);
486      // increment summary
487      this.totalRoots++;
488      return true;
489    } else {
490      return false;
491    }
492  }
493
494  /**
495   * Reset the roots Stream
496   * The roots Stream has to be reset separately because we do not
497   * gather data in the usual way using <code>scan()</code>.
498 */
499  public void resetRootsStream() {
500    rootsStream.resetData();
501    totalRoots = 0;
502  }
503
504  /**
505   * Handle a direct reference from the immortal space.
506   *
507   * @param addr The Address
508   * @return {@code true} if the given Address is in this subspace.
509   */
510  @Override
511  public boolean handleReferenceFromImmortalSpace(Address addr) {
512    if (subspace.addressInRange(addr)) {
513      // increment tile
514      int index = subspace.getIndex(addr);
515      refFromImmortalStream.increment(index, (short)1);
516      // increment summary
517      this.totalRefFromImmortal++;
518      return true;
519    } else {
520      return false;
521    }
522  }
523
524}