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