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}