1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import jdk.internal.misc.Unsafe; 25 import java.lang.reflect.Field; 26 27 /** 28 * Helper class to support testing of Unsafe.copyMemory and Unsafe.copySwapMemory 29 */ 30 public class CopyCommon { 31 private static final boolean DEBUG = Boolean.getBoolean("CopyCommon.DEBUG"); 32 33 public static final long KB = 1024; 34 public static final long MB = KB * 1024; 35 public static final long GB = MB * 1024; 36 37 static final int SMALL_COPY_SIZE = 32; 38 static final int BASE_ALIGNMENT = 16; 39 40 private static final Unsafe UNSAFE; 41 42 static { 43 try { 44 Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe"); 45 f.setAccessible(true); 46 UNSAFE = (jdk.internal.misc.Unsafe) f.get(null); 47 } catch (Exception e) { 48 throw new RuntimeException("Unable to get Unsafe instance.", e); 49 } 50 } 51 52 static long alignDown(long value, long alignment) { 53 return value & ~(alignment - 1); 54 } 55 56 static long alignUp(long value, long alignment) { 57 return (value + alignment - 1) & ~(alignment - 1); 58 } 59 60 static boolean isAligned(long value, long alignment) { 61 return value == alignDown(value, alignment); 62 } 63 64 CopyCommon() { 65 } 66 67 /** 68 * Generate verification data for a given offset 69 * 70 * The verification data is used to verify that the correct bytes 71 * have indeed been copied and byte swapped. 72 * 73 * The data is generated based on the offset (in bytes) into the 74 * source buffer. For a native buffer the offset is relative to 75 * the base pointer. For a heap array it is relative to the 76 * address of the first array element. 77 * 78 * This method will return the result of doing an elementSize byte 79 * read starting at offset (in bytes). 80 * 81 * @param offset offset into buffer 82 * @param elemSize size (in bytes) of the element 83 * 84 * @return the verification data, only the least significant 85 * elemSize*8 bits are set, zero extended 86 */ 87 private long getVerificationDataForOffset(long offset, long elemSize) { 88 byte[] bytes = new byte[(int)elemSize]; 89 90 for (long i = 0; i < elemSize; i++) { 91 bytes[(int)i] = (byte)(offset + i); 92 } 93 94 long o = UNSAFE.arrayBaseOffset(byte[].class); 95 96 switch ((int)elemSize) { 97 case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o)); 98 case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o)); 99 case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o)); 100 case 8: return UNSAFE.getLongUnaligned(bytes, o); 101 default: throw new IllegalArgumentException("Invalid element size: " + elemSize); 102 } 103 } 104 105 /** 106 * Verify byte swapped data 107 * 108 * @param ptr the data to verify 109 * @param srcOffset the srcOffset (in bytes) from which the copy started, 110 * used as key to regenerate the verification data 111 * @param dstOffset the offset (in bytes) in the array at which to start 112 * the verification, relative to the first element in the array 113 * @param size size (in bytes) of data to to verify 114 * @param elemSize size (in bytes) of the individual array elements 115 * 116 * @throws RuntimeException if an error is found 117 */ 118 private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) { 119 for (long offset = 0; offset < size; offset += elemSize) { 120 long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize); 121 long expected = byteSwap(expectedUnswapped, elemSize); 122 123 long actual = getArrayElem(ptr, dstOffset + offset, elemSize); 124 125 if (expected != actual) { 126 throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) + 127 " dstOffset: 0x" + Long.toHexString(dstOffset) + 128 " size: 0x" + Long.toHexString(size) + 129 " offset: 0x" + Long.toHexString(offset) + 130 " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) + 131 " expected: 0x" + Long.toHexString(expected) + 132 " != actual: 0x" + Long.toHexString(actual)); 133 } 134 } 135 } 136 137 /** 138 * Initialize an array with verification friendly data 139 * 140 * @param ptr pointer to the data to initialize 141 * @param size size (in bytes) of the data 142 * @param elemSize size (in bytes) of the individual elements 143 */ 144 private void initVerificationData(GenericPointer ptr, long size, long elemSize) { 145 for (long offset = 0; offset < size; offset++) { 146 byte data = (byte)getVerificationDataForOffset(offset, 1); 147 148 if (ptr.isOnHeap()) { 149 UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data); 150 } else { 151 UNSAFE.putByte(ptr.getOffset() + offset, data); 152 } 153 } 154 } 155 156 /** 157 * Allocate a primitive array 158 * 159 * @param size size (in bytes) of all the array elements (elemSize * length) 160 * @param elemSize the size of the array elements 161 * 162 * @return a newly allocated primitive array 163 */ 164 Object allocArray(long size, long elemSize) { 165 int length = (int)(size / elemSize); 166 167 switch ((int)elemSize) { 168 case 1: return new byte[length]; 169 case 2: return new short[length]; 170 case 4: return new int[length]; 171 case 8: return new long[length]; 172 default: 173 throw new IllegalArgumentException("Invalid element size: " + elemSize); 174 } 175 } 176 177 /** 178 * Get the value of a primitive array entry 179 * 180 * @param ptr pointer to the data 181 * @param offset offset (in bytes) of the array element, relative to the first element in the array 182 * 183 * @return the array element, as an unsigned long 184 */ 185 private long getArrayElem(GenericPointer ptr, long offset, long elemSize) { 186 if (ptr.isOnHeap()) { 187 Object o = ptr.getObject(); 188 int index = (int)(offset / elemSize); 189 190 if (o instanceof short[]) { 191 short[] arr = (short[])o; 192 return Short.toUnsignedLong(arr[index]); 193 } else if (o instanceof int[]) { 194 int[] arr = (int[])o; 195 return Integer.toUnsignedLong(arr[index]); 196 } else if (o instanceof long[]) { 197 long[] arr = (long[])o; 198 return arr[index]; 199 } else { 200 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName()); 201 } 202 } else { 203 long addr = ptr.getOffset() + offset; 204 205 switch ((int)elemSize) { 206 case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr)); 207 case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr)); 208 case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr)); 209 case 8: return UNSAFE.getLongUnaligned(null, addr); 210 default: throw new IllegalArgumentException("Invalid element size: " + elemSize); 211 } 212 } 213 } 214 215 private void putValue(long addr, long elemSize, long value) { 216 switch ((int)elemSize) { 217 case 1: UNSAFE.putByte(addr, (byte)value); break; 218 case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break; 219 case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break; 220 case 8: UNSAFE.putLongUnaligned(null, addr, value); break; 221 default: throw new IllegalArgumentException("Invalid element size: " + elemSize); 222 } 223 } 224 225 /** 226 * Get the size of the elements for an array 227 * 228 * @param o a primitive heap array 229 * 230 * @return the size (in bytes) of the individual array elements 231 */ 232 private long getArrayElemSize(Object o) { 233 if (o instanceof short[]) { 234 return 2; 235 } else if (o instanceof int[]) { 236 return 4; 237 } else if (o instanceof long[]) { 238 return 8; 239 } else { 240 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName()); 241 } 242 } 243 244 /** 245 * Byte swap a value 246 * 247 * @param value the value to swap, only the bytes*8 least significant bits are used 248 * @param size size (in bytes) of the value 249 * 250 * @return the byte swapped value in the bytes*8 least significant bits 251 */ 252 private long byteSwap(long value, long size) { 253 switch ((int)size) { 254 case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value)); 255 case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value)); 256 case 8: return Long.reverseBytes(value); 257 default: throw new IllegalArgumentException("Invalid element size: " + size); 258 } 259 } 260 261 /** 262 * Verify data which has *not* been byte swapped 263 * 264 * @param ptr the data to verify 265 * @param startOffset the offset (in bytes) at which to start the verification 266 * @param size size (in bytes) of the data to verify 267 * 268 * @throws RuntimeException if an error is found 269 */ 270 private void verifyUnswappedData(GenericPointer ptr, long startOffset, long srcOffset, long size) { 271 for (long i = 0; i < size; i++) { 272 byte expected = (byte)getVerificationDataForOffset(srcOffset + i, 1); 273 274 byte actual; 275 if (ptr.isOnHeap()) { 276 actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + startOffset + i); 277 } else { 278 actual = UNSAFE.getByte(ptr.getOffset() + startOffset + i); 279 } 280 281 if (expected != actual) { 282 throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) + 283 " srcOffset: 0x" + Long.toHexString(srcOffset) + 284 " size: 0x" + Long.toHexString(size) + 285 " i: 0x" + Long.toHexString(i) + 286 " expected: 0x" + Long.toHexString(expected) + 287 " != actual: 0x" + Long.toHexString(actual)); 288 } 289 } 290 } 291 292 293 /** 294 * Copy and byte swap data from the source to the destination 295 * 296 * This method will pre-populate the whole source and destination 297 * buffers with verification friendly data. It will then copy data 298 * to fill part of the destination buffer with data from the 299 * source, optionally byte swapping the copied elements on the 300 * fly. Some space (padding) will be left before and after the 301 * data in the destination buffer, which should not be 302 * touched/overwritten by the copy call. 303 * 304 * Note: Both source and destination buffers will be overwritten! 305 * 306 * @param src source buffer to copy from 307 * @param srcOffset the offset (in bytes) in the source buffer, relative to 308 * the first array element, at which to start reading data 309 * @param dst destination buffer to copy to 310 * @param dstOffset the offset (in bytes) in the destination 311 * buffer, relative to the first array element, at which to 312 * start writing data 313 * @param bufSize the size (in bytes) of the src and dst arrays 314 * @param copyBytes the size (in bytes) of the copy to perform, 315 * must be a multiple of elemSize 316 * @param elemSize the size (in bytes) of the elements 317 * @param swap true if elements should be byte swapped 318 * 319 * @throws RuntimeException if an error is found 320 */ 321 void testCopyGeneric(GenericPointer src, long srcOffset, 322 GenericPointer dst, long dstOffset, 323 long bufSize, long copyBytes, long elemSize, boolean swap) { 324 if (swap) { 325 if (!isAligned(copyBytes, elemSize)) { 326 throw new IllegalArgumentException( 327 "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")"); 328 } 329 if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) { 330 throw new IllegalArgumentException( 331 "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")"); 332 } 333 if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) { 334 throw new IllegalArgumentException( 335 "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")"); 336 } 337 } 338 339 if (srcOffset + copyBytes > bufSize) { 340 throw new IllegalArgumentException( 341 "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")"); 342 } 343 if (dstOffset + copyBytes > bufSize) { 344 throw new IllegalArgumentException( 345 "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")"); 346 } 347 348 // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes) 349 initVerificationData(src, bufSize, elemSize); 350 if (!src.equals(dst)) { 351 initVerificationData(dst, bufSize, elemSize); 352 } 353 354 if (DEBUG) { 355 System.out.println("===before==="); 356 for (int offset = 0; offset < bufSize; offset += elemSize) { 357 long srcValue = getArrayElem(src, offset, elemSize); 358 long dstValue = getArrayElem(dst, offset, elemSize); 359 360 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) + 361 " src=0x" + Long.toHexString(srcValue) + 362 " dst=0x" + Long.toHexString(dstValue)); 363 } 364 } 365 366 if (swap) { 367 // Copy & swap data into the middle of the destination buffer 368 UNSAFE.copySwapMemory(src.getObject(), 369 src.getOffset() + srcOffset, 370 dst.getObject(), 371 dst.getOffset() + dstOffset, 372 copyBytes, 373 elemSize); 374 } else { 375 // Copy & swap data into the middle of the destination buffer 376 UNSAFE.copyMemory(src.getObject(), 377 src.getOffset() + srcOffset, 378 dst.getObject(), 379 dst.getOffset() + dstOffset, 380 copyBytes); 381 } 382 383 if (DEBUG) { 384 System.out.println("===after==="); 385 for (int offset = 0; offset < bufSize; offset += elemSize) { 386 long srcValue = getArrayElem(src, offset, elemSize); 387 long dstValue = getArrayElem(dst, offset, elemSize); 388 389 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) + 390 " src=0x" + Long.toHexString(srcValue) + 391 " dst=0x" + Long.toHexString(dstValue)); 392 } 393 } 394 395 // Verify the the front padding is unchanged 396 verifyUnswappedData(dst, 0, 0, dstOffset); 397 398 if (swap) { 399 // Verify swapped data 400 verifySwappedData(dst, srcOffset, dstOffset, copyBytes, elemSize); 401 } else { 402 // Verify copied/unswapped data 403 verifyUnswappedData(dst, dstOffset, srcOffset, copyBytes); 404 } 405 406 // Verify that the back padding is unchanged 407 long frontAndCopyBytes = dstOffset + copyBytes; 408 long trailingBytes = bufSize - frontAndCopyBytes; 409 verifyUnswappedData(dst, frontAndCopyBytes, frontAndCopyBytes, trailingBytes); 410 } 411 412 /** 413 * Test various configurations of copying and optionally swapping data 414 * 415 * @param src the source buffer to copy from 416 * @param dst the destination buffer to copy to 417 * @param size size (in bytes) of the buffers 418 * @param elemSize size (in bytes) of the individual elements 419 * 420 * @throws RuntimeException if an error is found 421 */ 422 public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize, boolean swap) { 423 // offset in source from which to start reading data 424 for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) { 425 426 // offset in destination at which to start writing data 427 for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) { 428 429 // number of bytes to copy 430 long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset); 431 for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) { 432 try { 433 testCopyGeneric(src, srcOffset, dst, dstOffset, size, copyBytes, elemSize, swap); 434 } catch (RuntimeException e) { 435 // Wrap the exception in another exception to catch the relevant configuration data 436 throw new RuntimeException("testBufferPair: " + 437 "src=" + src + 438 " dst=" + dst + 439 " elemSize=0x" + Long.toHexString(elemSize) + 440 " copyBytes=0x" + Long.toHexString(copyBytes) + 441 " srcOffset=0x" + Long.toHexString(srcOffset) + 442 " dstOffset=0x" + Long.toHexString(dstOffset) + 443 " swap=" + swap, 444 e); 445 } 446 } 447 } 448 } 449 } 450 451 /** 452 * Test copying between various permutations of buffers 453 * 454 * @param buffers buffers to permute (src x dst) 455 * @param size size (in bytes) of buffers 456 * @param elemSize size (in bytes) of individual elements 457 * 458 * @throws RuntimeException if an error is found 459 */ 460 public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize, boolean swap) { 461 System.out.println("testPermuteBuffers(buffers, " + size + ", " + elemSize + ", " + swap + ")"); 462 for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) { 463 for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) { 464 testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize, swap); 465 } 466 } 467 } 468 469 /** 470 * Test copying of a specific element size 471 * 472 * @param size size (in bytes) of buffers to allocate 473 * @param elemSize size (in bytes) of individual elements 474 * 475 * @throws RuntimeException if an error is found 476 */ 477 private void testElemSize(long size, long elemSize, boolean swap) { 478 long buf1Raw = 0; 479 long buf2Raw = 0; 480 481 try { 482 buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); 483 long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT); 484 485 buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); 486 long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT); 487 488 GenericPointer[] buffers = { 489 new GenericPointer(buf1), 490 new GenericPointer(buf2), 491 new GenericPointer(allocArray(size, elemSize)), 492 new GenericPointer(allocArray(size, elemSize)) 493 }; 494 495 testPermuteBuffers(buffers, size, elemSize, swap); 496 } finally { 497 if (buf1Raw != 0) { 498 UNSAFE.freeMemory(buf1Raw); 499 } 500 if (buf2Raw != 0) { 501 UNSAFE.freeMemory(buf2Raw); 502 } 503 } 504 } 505 506 /** 507 * Verify that small copies work 508 */ 509 void testSmallCopy(boolean swap) { 510 int smallBufSize = SMALL_COPY_SIZE; 511 int minElemSize = swap ? 2 : 1; 512 int maxElemSize = swap ? 8 : 1; 513 514 // Test various element types and heap/native combinations 515 for (long elemSize = minElemSize; elemSize <= maxElemSize; elemSize <<= 1) { 516 testElemSize(smallBufSize, elemSize, swap); 517 } 518 } 519 520 521 /** 522 * Verify that large copies work 523 */ 524 void testLargeCopy(boolean swap) { 525 long size = 2 * GB + 8; 526 long bufRaw = 0; 527 528 // Check that a large native copy succeeds 529 try { 530 try { 531 bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); 532 } catch (OutOfMemoryError e) { 533 // Accept failure, skip test 534 return; 535 } 536 537 long buf = alignUp(bufRaw, BASE_ALIGNMENT); 538 539 if (swap) { 540 UNSAFE.copySwapMemory(null, buf, null, buf, size, 8); 541 } else { 542 UNSAFE.copyMemory(null, buf, null, buf, size); 543 } 544 } catch (Exception e) { 545 throw new RuntimeException("copy of large buffer failed (swap=" + swap + ")"); 546 } finally { 547 if (bufRaw != 0) { 548 UNSAFE.freeMemory(bufRaw); 549 } 550 } 551 } 552 553 /** 554 * Helper class to represent a "pointer" - either a heap array or 555 * a pointer to a native buffer. 556 * 557 * In the case of a native pointer, the Object is null and the offset is 558 * the absolute address of the native buffer. 559 * 560 * In the case of a heap object, the Object is a primitive array, and 561 * the offset will be set to the base offset to the first element, meaning 562 * the object and the offset together form a double-register pointer. 563 */ 564 static class GenericPointer { 565 private final Object o; 566 private final long offset; 567 568 private GenericPointer(Object o, long offset) { 569 this.o = o; 570 this.offset = offset; 571 } 572 573 public String toString() { 574 return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")"; 575 } 576 577 public boolean equals(Object other) { 578 if (!(other instanceof GenericPointer)) { 579 return false; 580 } 581 582 GenericPointer otherp = (GenericPointer)other; 583 584 return o == otherp.o && offset == otherp.offset; 585 } 586 587 GenericPointer(Object o) { 588 this(o, UNSAFE.arrayBaseOffset(o.getClass())); 589 } 590 591 GenericPointer(long offset) { 592 this(null, offset); 593 } 594 595 public boolean isOnHeap() { 596 return o != null; 597 } 598 599 public Object getObject() { 600 return o; 601 } 602 603 public long getOffset() { 604 return offset; 605 } 606 } 607 }