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.jikesrvm.mm.mmtk;
014    
015    import org.mmtk.plan.CollectorContext;
016    import org.mmtk.plan.TraceLocal;
017    import org.mmtk.utility.Constants;
018    import org.mmtk.utility.Log;
019    import org.jikesrvm.VM;
020    import org.jikesrvm.runtime.BootRecord;
021    import org.jikesrvm.scheduler.RVMThread;
022    import org.jikesrvm.mm.mminterface.MemoryManager;
023    
024    import org.vmmagic.unboxed.*;
025    import org.vmmagic.pragma.*;
026    
027    /**
028     * Scan the boot image for references using the boot image reference map
029     */
030    public class ScanBootImage implements Constants {
031    
032      private static final boolean DEBUG = false;
033      private static final boolean FILTER = true;
034    
035      private static final int LOG_CHUNK_BYTES = 12;
036      private static final int CHUNK_BYTES = 1<<LOG_CHUNK_BYTES;
037      private static final int LONGENCODING_MASK = 0x1;
038      private static final int RUN_MASK = 0x2;
039      private static final int MAX_RUN = (1<<BITS_IN_BYTE)-1;
040      private static final int LONGENCODING_OFFSET_BYTES = 4;
041      private static final int GUARD_REGION = LONGENCODING_OFFSET_BYTES + 1; /* long offset + run encoding */
042    
043      /* statistics */
044      static int roots = 0;
045      static int refs = 0;
046    
047      /****************************************************************************
048       *
049       * GC-time decoding (multi-threaded)
050       */
051    
052      /**
053       * Scan the boot image for object references.  Executed by
054       * all GC threads in parallel, with each doing a portion of the
055       * boot image.
056       *
057       * @param trace The trace object to which the roots should be added
058       */
059      @Inline
060      @Uninterruptible
061      public static void scanBootImage(TraceLocal trace) {
062        /* establish sentinals in map & image */
063        Address mapStart = BootRecord.the_boot_record.bootImageRMapStart;
064        Address mapEnd = BootRecord.the_boot_record.bootImageRMapEnd;
065        Address imageStart = BootRecord.the_boot_record.bootImageDataStart;
066    
067        /* figure out striding */
068        CollectorContext collector = RVMThread.getCurrentThread().getCollectorContext();
069        int stride = collector.parallelWorkerCount()<<LOG_CHUNK_BYTES;
070        int start = collector.parallelWorkerOrdinal()<<LOG_CHUNK_BYTES;
071        Address cursor = mapStart.plus(start);
072    
073        /* statistics */
074        roots = 0;
075        refs = 0;
076    
077        /* process chunks in parallel till done */
078        while (cursor.LT(mapEnd)) {
079          processChunk(cursor, imageStart, mapStart, mapEnd, trace);
080          cursor = cursor.plus(stride);
081        }
082    
083        /* print some debugging stats */
084        if (DEBUG) {
085          Log.write("<boot image");
086          Log.write(" roots: "); Log.write(roots);
087          Log.write(" refs: "); Log.write(refs);
088          Log.write(">");
089        }
090      }
091    
092      /**
093       * Process a chunk of encoded reference data, enqueuing each
094       * reference (optionally filtering them on whether they point
095       * outside the boot image).
096       *
097       * @param chunkStart The address of the first byte of encoded data
098       * @param imageStart The address of the start of the boot image
099       * @param mapStart The address of the start of the encoded reference map
100       * @param mapEnd The address of the end of the encoded reference map
101       * @param trace The <code>TraceLocal</code> into which roots should
102       * be enqueued.
103       */
104      @Inline
105      @Uninterruptible
106      private static void processChunk(Address chunkStart, Address imageStart,
107          Address mapStart, Address mapEnd, TraceLocal trace) {
108        int value;
109        Offset offset = Offset.zero();
110        Address cursor = chunkStart;
111        while ((value = (cursor.loadByte() & 0xff)) != 0) {
112          /* establish the offset */
113          if ((value & LONGENCODING_MASK) != 0) {
114            offset = decodeLongEncoding(cursor);
115            cursor = cursor.plus(LONGENCODING_OFFSET_BYTES);
116          } else {
117            offset = offset.plus(value & 0xfc);
118            cursor = cursor.plus(1);
119          }
120          /* figure out the length of the run, if any */
121          int runlength = 0;
122          if ((value & RUN_MASK) != 0) {
123            runlength = cursor.loadByte() & 0xff;
124            cursor = cursor.plus(1);
125          }
126          /* enqueue the specified slot or slots */
127          if (VM.VerifyAssertions) VM._assert(isAddressAligned(offset));
128          Address slot = imageStart.plus(offset);
129          if (DEBUG) refs++;
130          if (!FILTER || slot.loadAddress().GT(mapEnd)) {
131            if (DEBUG) roots++;
132            trace.processRootEdge(slot, false);
133          }
134          if (runlength != 0) {
135            for (int i = 0; i < runlength; i++) {
136              offset = offset.plus(BYTES_IN_ADDRESS);
137              slot = imageStart.plus(offset);
138              if (VM.VerifyAssertions) VM._assert(isAddressAligned(slot));
139              if (DEBUG) refs++;
140              if (!FILTER || slot.loadAddress().GT(mapEnd)) {
141                if (DEBUG) roots++;
142                if (ScanThread.VALIDATE_REFS) checkReference(slot);
143                trace.processRootEdge(slot, false);
144              }
145            }
146          }
147        }
148      }
149    
150      /**
151       * Check that a reference encountered during scanning is valid.  If
152       * the reference is invalid, dump stack and die.
153       *
154       * @param refaddr The address of the reference in question.
155       */
156      @Uninterruptible
157      private static void checkReference(Address refaddr) {
158        ObjectReference ref = org.mmtk.vm.VM.activePlan.global().loadObjectReference(refaddr);
159        if (!MemoryManager.validRef(ref)) {
160          Log.writeln();
161          Log.writeln("Invalid ref reported while scanning boot image");
162          Log.writeln();
163          Log.write(refaddr); Log.write(":"); Log.flush(); MemoryManager.dumpRef(ref);
164          Log.writeln();
165          Log.writeln("Dumping stack:");
166          RVMThread.dumpStack();
167          VM.sysFail("\n\nScanStack: Detected bad GC map; exiting RVM with fatal error");
168        }
169      }
170    
171      /**
172       * Return true if the given offset is address-aligned
173       * @param offset the offset to be check
174       * @return true if the offset is address aligned.
175       */
176      @Uninterruptible
177      private static boolean isAddressAligned(Offset offset) {
178        return (offset.toLong()>>LOG_BYTES_IN_ADDRESS)<<LOG_BYTES_IN_ADDRESS == offset.toLong();
179      }
180    
181      /**
182       * Return true if the given address is address-aligned
183       * @param address the address to be check
184       * @return true if the address is address aligned.
185       */
186      @Uninterruptible
187      private static boolean isAddressAligned(Address address) {
188        return (address.toLong()>>LOG_BYTES_IN_ADDRESS)<<LOG_BYTES_IN_ADDRESS == address.toLong();
189      }
190    
191      /****************************************************************************
192       *
193       * Build-time encoding (assumed to be single-threaded)
194       */
195    
196      /** */
197      private static int lastOffset = Integer.MIN_VALUE / 2;  /* bootstrap value */
198      private static int oldIndex = 0;
199      private static int codeIndex = 0;
200    
201      /* statistics */
202      private static int shortRefs = 0;
203      private static int runRefs = 0;
204      private static int longRefs = 0;
205      private static int startRefs = 0;
206    
207      /**
208       * Take a bytemap encoding of all references in the boot image, and
209       * produce an encoded byte array.  Return the total length of the
210       * encoding.
211       */
212      public static int encodeRMap(byte[] bootImageRMap, byte[] referenceMap,
213          int referenceMapLimit) {
214        for (int index = 0; index <= referenceMapLimit; index++) {
215          if (referenceMap[index] == 1) {
216            addOffset(bootImageRMap, index<<LOG_BYTES_IN_ADDRESS);
217          }
218        }
219        return codeIndex + 1;
220      }
221    
222      /**
223       * Print some basic statistics about the encoded references, for
224       * debugging purposes.
225       */
226      public static void encodingStats() {
227        if (DEBUG) {
228          Log.write("refs: "); Log.writeln(startRefs + shortRefs + longRefs + runRefs);
229          Log.write("start: "); Log.writeln(startRefs);
230          Log.write("short: "); Log.writeln(shortRefs);
231          Log.write("long: "); Log.writeln(longRefs);
232          Log.write("run: "); Log.writeln(runRefs);
233          Log.write("size: "); Log.writeln(codeIndex);
234        }
235      }
236    
237      /**
238       * Encode a given offset (distance from the start of the boot image)
239       * into the code array.
240       *
241       * @param code A byte array into which the value should be encoded
242       * @param offset The offset value to be encoded
243       */
244      private static void addOffset(byte[] code, int offset) {
245        if ((codeIndex ^ (codeIndex + GUARD_REGION)) >= CHUNK_BYTES) {
246          codeIndex = (codeIndex + GUARD_REGION) & ~(CHUNK_BYTES - 1);
247          oldIndex = codeIndex;
248          codeIndex = encodeLongEncoding(code, codeIndex, offset);
249          if (DEBUG) {
250            startRefs++;
251            Log.write("[chunk: "); Log.write(codeIndex);
252            Log.write(" offset: "); Log.write(offset);
253            Log.write(" last offset: "); Log.write(lastOffset);
254            Log.writeln("]");
255          }
256        } else {
257          int delta = offset - lastOffset;
258          if (VM.VerifyAssertions) VM._assert((delta & 0x3) == 0);
259          if (VM.VerifyAssertions) VM._assert(delta > 0);
260    
261          int currentrun = (code[codeIndex]) & 0xff;
262          if ((delta == BYTES_IN_ADDRESS) &&
263              (currentrun < MAX_RUN)) {
264            currentrun++;
265            code[codeIndex] = (byte) (currentrun & 0xff);
266            code[oldIndex] |= RUN_MASK;
267            if (DEBUG) runRefs++;
268          } else {
269            if (currentrun != 0) codeIndex++;
270            oldIndex = codeIndex;
271            if (delta < 1<<BITS_IN_BYTE) {
272              /* common case: single byte encoding */
273              code[codeIndex++] = (byte) (delta & 0xff);
274              if (DEBUG) shortRefs++;
275            } else {
276              /* else four byte encoding */
277              codeIndex = encodeLongEncoding(code, codeIndex, offset);
278              if (DEBUG) longRefs++;
279            }
280          }
281        }
282        if (offset != getOffset(code, oldIndex, lastOffset)) {
283          Log.write("offset: "); Log.writeln(offset);
284          Log.write("last offset: "); Log.writeln(lastOffset);
285          Log.write("offset: "); Log.writeln(getOffset(code, oldIndex, lastOffset));
286          Log.write("index: "); Log.writeln(oldIndex);
287          Log.write("index: "); Log.writeln(oldIndex & (CHUNK_BYTES - 1));
288          Log.writeln();
289          Log.write("1: "); Log.writeln(code[oldIndex]);
290          Log.write("2: "); Log.writeln(code[oldIndex+1]);
291          Log.write("3: "); Log.writeln(code[oldIndex+2]);
292          Log.write("4: "); Log.writeln(code[oldIndex+3]);
293          Log.write("5: "); Log.writeln(code[oldIndex+4]);
294          if (VM.VerifyAssertions)
295            VM._assert(offset == getOffset(code, oldIndex, lastOffset));
296        }
297        lastOffset = offset;
298      }
299    
300      /****************************************************************************
301       *
302       * Utility encoding and decoding methods
303       */
304    
305      /**
306       * Decode an encoded offset given the coded byte array, and index
307       * into it, and the current (last) offset.
308       *
309       * @param code A byte array containing the encoded value
310       * @param index The offset into the code array from which to
311       * commence decoding
312       * @param lastOffset The current (last) encoded offset
313       * @return The next offset, which is either explicitly encoded in
314       * the byte array or inferred from an encoded delta and the last
315       * offset.
316     */
317      private static int getOffset(byte[] code, int index, int lastOffset) {
318        if ((code[index] & RUN_MASK) == RUN_MASK) {
319          return lastOffset + BYTES_IN_WORD;
320        } else {
321          if (((index & (CHUNK_BYTES - 1)) == 0) ||
322              ((code[index] &LONGENCODING_MASK) == LONGENCODING_MASK)) {
323            return decodeLongEncoding(code, index);
324          } else {
325            return lastOffset + ((code[index]) & 0xff);
326          }
327        }
328      }
329    
330      /**
331       * Decode a 4-byte encoding, taking a pointer to the first byte of
332       * the encoding, and returning the encoded value as an <code>Offset</code>
333       *
334       * @param cursor A pointer to the first byte of encoded data
335       * @return The encoded value as an <code>Offset</code>
336       */
337      @Inline
338      @Uninterruptible
339      private static Offset decodeLongEncoding(Address cursor) {
340        int value;
341        value  = (cursor.loadByte())                                              & 0x000000fc;
342        value |= (cursor.loadByte(Offset.fromIntSignExtend(1))<<BITS_IN_BYTE)     & 0x0000ff00;
343        value |= (cursor.loadByte(Offset.fromIntSignExtend(2))<<(2*BITS_IN_BYTE)) & 0x00ff0000;
344        value |= (cursor.loadByte(Offset.fromIntSignExtend(3))<<(3*BITS_IN_BYTE)) & 0xff000000;
345        return Offset.fromIntSignExtend(value);
346      }
347    
348      /**
349       * Decode a 4-byte encoding, taking a byte array and an index into
350       * it and returning the encoded value as an integer
351       *
352       * @param code A byte array containing the encoded value
353       * @param index The offset into the code array from which to
354       * commence decoding
355       * @return The encoded value as an integer
356       */
357      @Inline
358      @Uninterruptible
359      private static int decodeLongEncoding(byte[] code, int index) {
360        int value;
361        value  = (code[index])                     & 0x000000fc;
362        value |= (code[index+1]<<BITS_IN_BYTE)     & 0x0000ff00;
363        value |= (code[index+2]<<(2*BITS_IN_BYTE)) & 0x00ff0000;
364        value |= (code[index+3]<<(3*BITS_IN_BYTE)) & 0xff000000;
365        return value;
366      }
367    
368      /**
369       * Encode a 4-byte encoding, taking a byte array, the current index into
370       * it, and the value to be encoded.
371       *
372       * @param code A byte array to contain the encoded value
373       * @param index The current offset into the code array
374       * @param value The value to be encoded
375       * @return The updated index into the code array
376       */
377      private static int encodeLongEncoding(byte[] code, int index, int value) {
378        code[index++] = (byte) ((value & 0xff) | LONGENCODING_MASK);
379        value = value >>> BITS_IN_BYTE;
380        code[index++] = (byte) (value & 0xff);
381        value = value >>> BITS_IN_BYTE;
382        code[index++] = (byte) (value & 0xff);
383        value = value >>> BITS_IN_BYTE;
384        code[index++] = (byte) (value & 0xff);
385        return index;
386      }
387    
388    }