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