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;
014    
015    import org.jikesrvm.mm.mminterface.Barriers;
016    import org.jikesrvm.runtime.Entrypoints;
017    import org.jikesrvm.runtime.Magic;
018    import org.jikesrvm.scheduler.Synchronization;
019    import org.vmmagic.pragma.Inline;
020    import org.vmmagic.pragma.Interruptible;
021    import org.vmmagic.pragma.NoInline;
022    import org.vmmagic.pragma.Uninterruptible;
023    import org.vmmagic.pragma.UninterruptibleNoWarn;
024    import org.vmmagic.unboxed.Offset;
025    
026    /**
027     *  Various service utilities.  This is a common place for some shared utility routines
028     */
029    @Uninterruptible
030    public class Services implements SizeConstants {
031      /**
032       * Biggest buffer you would possibly need for {@link org.jikesrvm.scheduler.RVMThread#dump(char[], int)}
033       * Modify this if you modify that method.
034       */
035      public static final int MAX_DUMP_LEN =
036        10 /* for thread ID  */ + 7 + 5 + 5 + 11 + 5 + 10 + 13 + 17 + 10;
037    
038      /** Pre-allocate the dump buffer, since dump() might get called inside GC. */
039      private static final char[] dumpBuffer = new char[MAX_DUMP_LEN];
040    
041      @SuppressWarnings({"unused", "CanBeFinal", "UnusedDeclaration"})// accessed via EntryPoints
042      private static int dumpBufferLock = 0;
043    
044      /** Reset at boot time. */
045      private static Offset dumpBufferLockOffset = Offset.max();
046    
047      /**
048       * A map of hexadecimal digit values to their character representations.
049       * <P>
050       * XXX We currently only use '0' through '9'.  The rest are here pending
051       * possibly merging this code with the similar code in Log.java, or breaking
052       * this code out into a separate utility class.
053       */
054      private static final char [] hexDigitCharacter =
055      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
056        'f' };
057    
058      /**
059       * How many characters we need to have in a buffer for building string
060       * representations of <code>long</code>s, such as {@link #intBuffer}. A
061       * <code>long</code> is a signed 64-bit integer in the range -2^63 to
062       * 2^63+1. The number of digits in the decimal representation of 2^63 is
063       * ceiling(log10(2^63)) == ceiling(63 * log10(2)) == 19. An extra character
064       * may be required for a minus sign (-). So the maximum number of characters
065       * is 20.
066       */
067      private static final int INT_BUFFER_SIZE = 20;
068    
069      /** A buffer for building string representations of <code>long</code>s */
070      private static final char [] intBuffer = new char[INT_BUFFER_SIZE];
071    
072      /** A lock for {@link #intBuffer} */
073      @SuppressWarnings({"unused", "CanBeFinal", "UnusedDeclaration"})// accessed via EntryPoints
074      private static int intBufferLock = 0;
075    
076      /** The offset of {@link #intBufferLock} in this class's TIB.
077       *  This is set properly at boot time, even though it's a
078       *  <code>private</code> variable. . */
079      private static Offset intBufferLockOffset = Offset.max();
080    
081      /**
082       * Called during the boot sequence, any time before we go multi-threaded. We
083       * do this so that we can leave the lockOffsets set to -1 until the VM
084       * actually needs the locking (and is running multi-threaded).
085       */
086      public static void boot() {
087        dumpBufferLockOffset = Entrypoints.dumpBufferLockField.getOffset();
088        intBufferLockOffset = Entrypoints.intBufferLockField.getOffset();
089      }
090    
091      public static char[] grabDumpBuffer() {
092        if (!dumpBufferLockOffset.isMax()) {
093          while (!Synchronization.testAndSet(Magic.getJTOC(), dumpBufferLockOffset, 1)) {
094            ;
095          }
096        }
097        return dumpBuffer;
098      }
099    
100      public static void releaseDumpBuffer() {
101        if (!dumpBufferLockOffset.isMax()) {
102          Synchronization.fetchAndStore(Magic.getJTOC(), dumpBufferLockOffset, 0);
103        }
104      }
105    
106    
107      /** Copy a String into a character array.
108       *  <p>
109       *  This function may be called during GC and may be used in conjunction
110       *  with the MMTk {@link org.mmtk.utility.Log} class.   It avoids write barriers and allocation.
111       *  <p>
112       *  XXX This function should probably be moved to a sensible location where
113       *   we can use it as a utility.   Suggestions welcome.
114       *  <P>
115       *
116       * @param dest char array to copy into.
117       * @param destOffset Offset into <code>dest</code> where we start copying
118       *
119       * @return 1 plus the index of the last character written.  If we were to
120       *         write zero characters (which we won't) then we would return
121       *         <code>offset</code>.  This is intended to represent the first
122       *         unused position in the array <code>dest</code>.  However, it also
123       *         serves as a pseudo-overflow check:  It may have the value
124       *         <code>dest.length</code>, if the array <code>dest</code> was
125       *         completely filled by the call, or it may have a value greater
126       *         than <code>dest.length</code>, if the info needs more than
127       *         <code>dest.length - offset</code> characters of space.
128       *
129       * @return  -1 if <code>offset</code> is negative.
130       *
131       * the MMTk {@link org.mmtk.utility.Log} class).
132       */
133      public static int sprintf(char[] dest, int destOffset, String s) {
134        final char[] sArray = java.lang.JikesRVMSupport.getBackingCharArray(s);
135        return sprintf(dest, destOffset, sArray);
136      }
137    
138      public static int sprintf(char[] dest, int destOffset, char[] src) {
139        return sprintf(dest, destOffset, src, 0, src.length);
140      }
141    
142      /**
143       *  Copies characters from <code>src</code> into the destination character
144       *  array <code>dest</code>.<p>
145       *
146       *  The first character to be copied is at index <code>srcBegin</code>; the
147       *  last character to be copied is at index <code>srcEnd-1</code>.  (This is
148       *  the same convention as followed by java.lang.String#getChars).
149       *
150       * @param dest char array to copy into.
151       * @param destOffset Offset into <code>dest</code> where we start copying
152       * @param src Char array to copy from
153       * @param srcStart index of the first character of <code>src</code> to copy.
154       * @param srcEnd index after the last character of <code>src</code> to copy.
155       */
156      public static int sprintf(char[] dest, int destOffset, char[] src, int srcStart, int srcEnd) {
157        for (int i = srcStart; i < srcEnd; ++i) {
158          char nextChar = getArrayNoBarrier(src, i);
159          destOffset = sprintf(dest, destOffset, nextChar);
160        }
161        return destOffset;
162      }
163    
164      public static int sprintf(char[] dest, int destOffset, char c) {
165        if (destOffset < 0) {
166          // bounds check
167          return -1;
168        }
169    
170        if (destOffset < dest.length) {
171          setArrayNoBarrier(dest, destOffset, c);
172        }
173        return destOffset + 1;
174      }
175    
176      /**
177       * Copy the printed decimal representation of a long into
178       * a character array.  The value is not padded and no
179       * thousands separator is copied.  If the value is negative a
180       * leading minus sign (-) is copied.
181       * <p>
182       * This function may be called during GC and may be used in conjunction
183       * with the Log class.   It avoids write barriers and allocation.
184       * <p>
185       * XXX This function should probably be moved to a sensible location where
186       *  we can use it as a utility.   Suggestions welcome.
187       * <p>
188       * XXX This method's implementation is stolen from the {@link org.mmtk.utility.Log} class.
189       *
190       * @param dest char array to copy into.
191       * @param offset Offset into <code>dest</code> where we start copying
192       *
193       * @return 1 plus the index of the last character written.  If we were to
194       *         write zero characters (which we won't) then we would return
195       *         <code>offset</code>.  This is intended to represent the first
196       *         unused position in the array <code>dest</code>.  However, it also
197       *         serves as a pseudo-overflow check:  It may have the value
198       *         <code>dest.length</code>, if the array <code>dest</code> was
199       *         completely filled by the call, or it may have a value greater
200       *         than <code>dest.length</code>, if the info needs more than
201       *         <code>dest.length - offset</code> characters of space.
202       *
203       * @return  -1 if <code>offset</code> is negative.
204       */
205      public static int sprintf(char[] dest, int offset, long l) {
206        boolean negative = l < 0;
207        int nextDigit;
208        char nextChar;
209        int index = INT_BUFFER_SIZE - 1;
210        char[] intBuffer = grabIntBuffer();
211    
212        nextDigit = (int) (l % 10);
213        nextChar = getArrayNoBarrier(hexDigitCharacter, negative ? -nextDigit : nextDigit);
214        setArrayNoBarrier(intBuffer, index--, nextChar);
215        l = l / 10;
216    
217        while (l != 0) {
218          nextDigit = (int) (l % 10);
219          nextChar = getArrayNoBarrier(hexDigitCharacter, negative ? -nextDigit : nextDigit);
220          setArrayNoBarrier(intBuffer, index--, nextChar);
221          l = l / 10;
222        }
223    
224        if (negative) {
225         setArrayNoBarrier(intBuffer, index--, '-');
226        }
227    
228        int newOffset = sprintf(dest, offset, intBuffer, index + 1, INT_BUFFER_SIZE);
229        releaseIntBuffer();
230        return newOffset;
231      }
232    
233      /**
234       * Get exclusive access to {@link #intBuffer}, the buffer for building
235       * string representations of integers.
236       */
237      private static char[] grabIntBuffer() {
238        if (!intBufferLockOffset.isMax()) {
239          while (!Synchronization.testAndSet(Magic.getJTOC(), intBufferLockOffset, 1)) {
240            ;
241          }
242        }
243        return intBuffer;
244      }
245    
246      /**
247       * Release {@link #intBuffer}, the buffer for building string
248       * representations of integers.
249       */
250      private static void releaseIntBuffer() {
251        if (!intBufferLockOffset.isMax()) {
252          Synchronization.fetchAndStore(Magic.getJTOC(), intBufferLockOffset, 0);
253        }
254      }
255    
256      /**
257       * Utility printing function.
258       * @param i
259       * @param blank
260       */
261      @Interruptible
262      public static String getHexString(int i, boolean blank) {
263        StringBuilder buf = new StringBuilder(8);
264        for (int j = 0; j < 8; j++, i <<= 4) {
265          int n = i >>> 28;
266          if (blank && (n == 0) && (j != 7)) {
267            buf.append(' ');
268          } else {
269            buf.append(Character.forDigit(n, 16));
270            blank = false;
271          }
272        }
273        return buf.toString();
274      }
275    
276      @NoInline
277      public static void breakStub() {
278      }
279    
280      static void println() { VM.sysWrite("\n"); }
281    
282      static void print(String s) { VM.sysWrite(s); }
283    
284      static void println(String s) {
285        print(s);
286        println();
287      }
288    
289      static void print(int i) { VM.sysWrite(i); }
290    
291      static void println(int i) {
292        print(i);
293        println();
294      }
295    
296      static void print(String s, int i) {
297        print(s);
298        print(i);
299      }
300    
301      static void println(String s, int i) {
302        print(s, i);
303        println();
304      }
305    
306      public static void percentage(int numerator, int denominator, String quantity) {
307        print("\t");
308        if (denominator > 0) {
309          print((int) (((numerator) * 100.0) / (denominator)));
310        } else {
311          print("0");
312        }
313        print("% of ");
314        println(quantity);
315      }
316    
317      static void percentage(long numerator, long denominator, String quantity) {
318        print("\t");
319        if (denominator > 0L) {
320          print((int) (((numerator) * 100.0) / (denominator)));
321        } else {
322          print("0");
323        }
324        print("% of ");
325        println(quantity);
326      }
327    
328      /**
329       * Sets an element of a object array without possibly losing control.
330       * NB doesn't perform checkstore or array index checking.
331       *
332       * @param dst the destination array
333       * @param index the index of the element to set
334       * @param value the new value for the element
335       */
336      @UninterruptibleNoWarn("Interruptible code not reachable at runtime")
337      @Inline
338      public static void setArrayUninterruptible(Object[] dst, int index, Object value) {
339        if (VM.runningVM) {
340          if (Barriers.NEEDS_OBJECT_ASTORE_BARRIER) {
341            Barriers.objectArrayWrite(dst, index, value);
342          } else {
343            Magic.setObjectAtOffset(dst, Offset.fromIntZeroExtend(index << LOG_BYTES_IN_ADDRESS), value);
344          }
345        } else {
346          dst[index] = value;
347        }
348      }
349    
350      /**
351       * Sets an element of a char array without invoking any write
352       * barrier.  This method is called by the Log method, as it will be
353       * used during garbage collection and needs to manipulate character
354       * arrays without causing a write barrier operation.
355       *
356       * @param dst the destination array
357       * @param index the index of the element to set
358       * @param value the new value for the element
359       */
360      public static void setArrayNoBarrier(char[] dst, int index, char value) {
361        if (VM.runningVM)
362          Magic.setCharAtOffset(dst, Offset.fromIntZeroExtend(index << LOG_BYTES_IN_CHAR), value);
363        else
364          dst[index] = value;
365      }
366    
367      /**
368       * Gets an element of an Object array without invoking any read
369       * barrier or performing bounds checks.
370       *
371       * @param src the source array
372       * @param index the natural array index of the element to get
373       * @return the new value of element
374       */
375      public static Object getArrayNoBarrier(Object[] src, int index) {
376        if (VM.runningVM)
377          return Magic.getObjectAtOffset(src, Offset.fromIntZeroExtend(index << LOG_BYTES_IN_ADDRESS));
378        else
379          return src[index];
380      }
381    
382      /**
383       * Gets an element of an int array without invoking any read barrier
384       * or performing bounds checks.
385       *
386       * @param src the source array
387       * @param index the natural array index of the element to get
388       * @return the new value of element
389       */
390      public static int getArrayNoBarrier(int[] src, int index) {
391        if (VM.runningVM)
392          return Magic.getIntAtOffset(src, Offset.fromIntZeroExtend(index << LOG_BYTES_IN_INT));
393        else
394          return src[index];
395      }
396    
397      /**
398       * Gets an element of a char array without invoking any read barrier
399       * or performing bounds check.
400       *
401       * @param src the source array
402       * @param index the natural array index of the element to get
403       * @return the new value of element
404       */
405      public static char getArrayNoBarrier(char[] src, int index) {
406        if (VM.runningVM)
407          return Magic.getCharAtOffset(src, Offset.fromIntZeroExtend(index << LOG_BYTES_IN_CHAR));
408        else
409          return src[index];
410      }
411    
412      /**
413       * Gets an element of a byte array without invoking any read barrier
414       * or bounds check.
415       *
416       * @param src the source array
417       * @param index the natural array index of the element to get
418       * @return the new value of element
419       */
420      public static byte getArrayNoBarrier(byte[] src, int index) {
421        if (VM.runningVM)
422          return Magic.getByteAtOffset(src, Offset.fromIntZeroExtend(index));
423        else
424          return src[index];
425      }
426    
427      /**
428       * Gets an element of an array of byte arrays without causing the potential
429       * thread switch point that array accesses normally cause.
430       *
431       * @param src the source array
432       * @param index the index of the element to get
433       * @return the new value of element
434       */
435      public static byte[] getArrayNoBarrier(byte[][] src, int index) {
436        if (VM.runningVM)
437          return Magic.addressAsByteArray(Magic.objectAsAddress(Magic.getObjectAtOffset(src, Offset.fromIntZeroExtend(index << LOG_BYTES_IN_ADDRESS))));
438        else
439          return src[index];
440      }
441    }