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 * @test
29 * @summary Test Unsafe.copyMemory
30 * @modules java.base/jdk.internal.misc
31 */
32 public class CopyMemory {
33 private static final boolean DEBUG = Boolean.getBoolean("CopyMemory.DEBUG");
34
35 public static final long KB = 1024;
36 public static final long MB = KB * 1024;
37 public static final long GB = MB * 1024;
38
39 private static final Unsafe UNSAFE;
40 private static final int SMALL_COPY_SIZE = 32;
41 private static final int BASE_ALIGNMENT = 16;
42
43 static {
44 try {
45 Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
46 f.setAccessible(true);
47 UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
48 } catch (Exception e) {
49 throw new RuntimeException("Unable to get Unsafe instance.", e);
50 }
51 }
52
53 private static long alignDown(long value, long alignment) {
54 return value & ~(alignment - 1);
55 }
56
57 private static long alignUp(long value, long alignment) {
58 return (value + alignment - 1) & ~(alignment - 1);
59 }
60
61 private static boolean isAligned(long value, long alignment) {
62 return value == alignDown(value, alignment);
63 }
64
65 private CopyMemory() {
66 }
67
68 /**
69 * Generate verification data for a given offset
70 *
71 * The verification data is used to verify that the correct bytes
72 * have indeed been copied and byte swapped.
73 *
74 * The data is generated based on the offset (in bytes) into the
75 * source buffer. For a native buffer the offset is relative to
76 * the base pointer. For a heap array it is relative to the
77 * address of the first array element.
78 *
79 * This method will return the result of doing an elementSize byte
80 * read starting at offset (in bytes).
81 *
82 * @param offset offset into buffer
83 * @param elemSize size (in bytes) of the element
84 *
85 * @return the verification data, only the least significant
86 * elemSize*8 bits are set, zero extended
87 */
88 private long getVerificationDataForOffset(long offset, long elemSize) {
89 byte[] bytes = new byte[(int)elemSize];
90
91 for (long i = 0; i < elemSize; i++) {
92 bytes[(int)i] = (byte)(offset + i);
93 }
94
95 long o = UNSAFE.arrayBaseOffset(byte[].class);
96
97 switch ((int)elemSize) {
98 case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o));
99 case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o));
100 case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o));
101 case 8: return UNSAFE.getLongUnaligned(bytes, o);
102 default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
103 }
104 }
105
106 /**
107 * Verify byte swapped data
108 *
109 * @param ptr the data to verify
110 * @param srcOffset the srcOffset (in bytes) from which the copy started,
111 * used as key to regenerate the verification data
112 * @param dstOffset the offset (in bytes) in the array at which to start
113 * the verification, relative to the first element in the array
114 * @param size size (in bytes) of data to to verify
115 * @param elemSize size (in bytes) of the individual array elements
116 *
117 * @throws RuntimeException if an error is found
118 */
119 private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) {
120 for (long offset = 0; offset < size; offset += elemSize) {
121 long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize);
122 long expected = byteSwap(expectedUnswapped, elemSize);
123
124 long actual = getArrayElem(ptr, dstOffset + offset, elemSize);
125
126 if (expected != actual) {
127 throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) +
128 " dstOffset: 0x" + Long.toHexString(dstOffset) +
129 " size: 0x" + Long.toHexString(size) +
130 " offset: 0x" + Long.toHexString(offset) +
131 " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) +
132 " expected: 0x" + Long.toHexString(expected) +
133 " != actual: 0x" + Long.toHexString(actual));
134 }
135 }
136 }
137
138 /**
139 * Initialize an array with verification friendly data
140 *
141 * @param ptr pointer to the data to initialize
142 * @param size size (in bytes) of the data
143 * @param elemSize size (in bytes) of the individual elements
144 */
145 private void initVerificationData(GenericPointer ptr, long size, long elemSize) {
146 for (long offset = 0; offset < size; offset++) {
147 byte data = (byte)getVerificationDataForOffset(offset, 1);
148
149 if (ptr.isOnHeap()) {
150 UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data);
151 } else {
152 UNSAFE.putByte(ptr.getOffset() + offset, data);
153 }
154 }
155 }
156
157 /**
158 * Allocate a primitive array
159 *
160 * @param size size (in bytes) of all the array elements (elemSize * length)
161 * @param elemSize the size of the array elements
162 *
163 * @return a newly allocated primitive array
164 */
165 Object allocArray(long size, long elemSize) {
166 int length = (int)(size / elemSize);
167
168 switch ((int)elemSize) {
169 case 1: return new byte[length];
170 case 2: return new short[length];
171 case 4: return new int[length];
172 case 8: return new long[length];
173 default:
174 throw new IllegalArgumentException("Invalid element size: " + elemSize);
175 }
176 }
177
178 /**
179 * Get the value of a primitive array entry
180 *
181 * @param ptr pointer to the data
182 * @param offset offset (in bytes) of the array element, relative to the first element in the array
183 *
184 * @return the array element, as an unsigned long
185 */
186 private long getArrayElem(GenericPointer ptr, long offset, long elemSize) {
187 if (ptr.isOnHeap()) {
188 Object o = ptr.getObject();
189 int index = (int)(offset / elemSize);
190
191 if (o instanceof short[]) {
192 short[] arr = (short[])o;
193 return Short.toUnsignedLong(arr[index]);
194 } else if (o instanceof int[]) {
195 int[] arr = (int[])o;
196 return Integer.toUnsignedLong(arr[index]);
197 } else if (o instanceof long[]) {
198 long[] arr = (long[])o;
199 return arr[index];
200 } else {
201 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
202 }
203 } else {
204 long addr = ptr.getOffset() + offset;
205
206 switch ((int)elemSize) {
207 case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr));
208 case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr));
209 case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr));
210 case 8: return UNSAFE.getLongUnaligned(null, addr);
211 default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
212 }
213 }
214 }
215
216 private void putValue(long addr, long elemSize, long value) {
217 switch ((int)elemSize) {
218 case 1: UNSAFE.putByte(addr, (byte)value); break;
219 case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break;
220 case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break;
221 case 8: UNSAFE.putLongUnaligned(null, addr, value); break;
222 default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
223 }
224 }
225
226 /**
227 * Get the size of the elements for an array
228 *
229 * @param o a primitive heap array
230 *
231 * @return the size (in bytes) of the individual array elements
232 */
233 private long getArrayElemSize(Object o) {
234 if (o instanceof short[]) {
235 return 2;
236 } else if (o instanceof int[]) {
237 return 4;
238 } else if (o instanceof long[]) {
239 return 8;
240 } else {
241 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
242 }
243 }
244
245 /**
246 * Byte swap a value
247 *
248 * @param value the value to swap, only the bytes*8 least significant bits are used
249 * @param size size (in bytes) of the value
250 *
251 * @return the byte swapped value in the bytes*8 least significant bits
252 */
253 private long byteSwap(long value, long size) {
254 switch ((int)size) {
255 case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value));
256 case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value));
257 case 8: return Long.reverseBytes(value);
258 default: throw new IllegalArgumentException("Invalid element size: " + size);
259 }
260 }
261
262 /**
263 * Verify data which has *not* been byte swapped
264 *
265 * @param ptr the data to verify
266 * @param startOffset the offset (in bytes) at which to start the verification
267 * @param size size (in bytes) of the data to verify
268 *
269 * @throws RuntimeException if an error is found
270 */
271 private void verifyUnswappedData(GenericPointer ptr, long startOffset, long srcOffset, long size) {
272 for (long i = 0; i < size; i++) {
273 byte expected = (byte)getVerificationDataForOffset(srcOffset + i, 1);
274
275 byte actual;
276 if (ptr.isOnHeap()) {
277 actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + startOffset + i);
278 } else {
279 actual = UNSAFE.getByte(ptr.getOffset() + startOffset + i);
280 }
281
282 if (expected != actual) {
283 throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) +
284 " srcOffset: 0x" + Long.toHexString(srcOffset) +
285 " size: 0x" + Long.toHexString(size) +
286 " i: 0x" + Long.toHexString(i) +
287 " expected: 0x" + Long.toHexString(expected) +
288 " != actual: 0x" + Long.toHexString(actual));
289 }
290 }
291 }
292
293
294 /**
295 * Copy and byte swap data from the source to the destination
296 *
297 * This method will pre-populate the whole source and destination
298 * buffers with verification friendly data. It will then use
299 * copypMemory to fill part of the destination buffer with
300 * data from the source. Some space (padding) will be
301 * left before and after the data in the destination buffer, which
302 * should not be 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 *
317 * @throws RuntimeException if an error is found
318 */
319 private void testCopy(GenericPointer src, long srcOffset,
320 GenericPointer dst, long dstOffset,
321 long bufSize, long copyBytes) {
322 if (srcOffset + copyBytes > bufSize) {
323 throw new IllegalArgumentException(
324 "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
325 }
326 if (dstOffset + copyBytes > bufSize) {
327 throw new IllegalArgumentException(
328 "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
329 }
330
331 // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes)
332 initVerificationData(src, bufSize, 1);
333 if (!src.equals(dst)) {
334 initVerificationData(dst, bufSize, 1);
335 }
336
337 if (DEBUG) {
338 System.out.println("===before===");
339 for (int offset = 0; offset < bufSize; offset++) {
340 long srcValue = getArrayElem(src, offset, 1);
341 long dstValue = getArrayElem(dst, offset, 1);
342
343 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
344 " src=0x" + Long.toHexString(srcValue) +
345 " dst=0x" + Long.toHexString(dstValue));
346 }
347 }
348
349 // Copy & swap data into the middle of the destination buffer
350 UNSAFE.copyMemory(src.getObject(),
351 src.getOffset() + srcOffset,
352 dst.getObject(),
353 dst.getOffset() + dstOffset,
354 copyBytes);
355
356 if (DEBUG) {
357 System.out.println("===after===");
358 for (int offset = 0; offset < bufSize; offset++) {
359 long srcValue = getArrayElem(src, offset, 1);
360 long dstValue = getArrayElem(dst, offset, 1);
361
362 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
363 " src=0x" + Long.toHexString(srcValue) +
364 " dst=0x" + Long.toHexString(dstValue));
365 }
366 }
367
368 // Verify the the front padding is unchanged
369 verifyUnswappedData(dst, 0, 0, dstOffset);
370
371 // Verify copied data
372 verifyUnswappedData(dst, dstOffset, srcOffset, copyBytes);
373
374 // Verify that the back back padding is unchanged
375 long frontAndDataBytes = dstOffset + copyBytes;
376 long trailingBytes = bufSize - frontAndDataBytes;
377 verifyUnswappedData(dst, frontAndDataBytes, frontAndDataBytes, trailingBytes);
378 }
379
380 /**
381 * Test various configurations copying from one buffer to the other
382 *
383 * @param src the source buffer to copy from
384 * @param dst the destination buffer to copy to
385 * @param size size (in bytes) of the buffers
386 * @param elemSize size (in bytes) of the individual elements
387 *
388 * @throws RuntimeException if an error is found
389 */
390 public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) {
391 // offset in source from which to start reading data
392 for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) {
393
394 // offset in destination at which to start writing data
395 for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) {
396
397 // number of bytes to copy
398 long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset);
399 for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) {
400 try {
401 testCopy(src, srcOffset, dst, dstOffset, size, copyBytes);
402 } catch (RuntimeException e) {
403 // Wrap the exception in another exception to catch the relevant configuration data
404 throw new RuntimeException("testBufferPair: " +
405 "src=" + src +
406 " dst=" + dst +
407 " elemSize=0x" + Long.toHexString(elemSize) +
408 " copyBytes=0x" + Long.toHexString(copyBytes) +
409 " srcOffset=0x" + Long.toHexString(srcOffset) +
410 " dstOffset=0x" + Long.toHexString(dstOffset),
411 e);
412 }
413 }
414 }
415 }
416 }
417
418 /**
419 * Test copying between various permutations of buffers
420 *
421 * @param buffers buffers to permute (src x dst)
422 * @param size size (in bytes) of buffers
423 * @param elemSize size (in bytes) of individual elements
424 *
425 * @throws RuntimeException if an error is found
426 */
427 public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) {
428 for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) {
429 for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) {
430 testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize);
431 }
432 }
433 }
434
435 /**
436 * Test copying of a specific element size
437 *
438 * @param size size (in bytes) of buffers to allocate
439 * @param elemSize size (in bytes) of individual elements
440 *
441 * @throws RuntimeException if an error is found
442 */
443 private void testElemSize(long size, long elemSize) {
444 long buf1Raw = 0;
445 long buf2Raw = 0;
446
447 try {
448 buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
449 long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT);
450
451 buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
452 long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT);
453
454 GenericPointer[] buffers = {
455 new GenericPointer(buf1),
456 new GenericPointer(buf2),
457 new GenericPointer(allocArray(size, elemSize)),
458 new GenericPointer(allocArray(size, elemSize))
459 };
460
461 testPermuteBuffers(buffers, size, elemSize);
462 } finally {
463 if (buf1Raw != 0) {
464 UNSAFE.freeMemory(buf1Raw);
465 }
466 if (buf2Raw != 0) {
467 UNSAFE.freeMemory(buf2Raw);
468 }
469 }
470 }
471
472 /**
473 * Verify that small copies work
474 */
475 private void testSmallCopy() {
476 int smallBufSize = SMALL_COPY_SIZE;
477
478 testElemSize(smallBufSize, 1);
479 }
480
481
482 /**
483 * Verify that large copies work
484 */
485 private void testLargeCopy() {
486 long size = 2 * GB + 8;
487 long bufRaw = 0;
488
489 // Check that a large native copy succeeds
490 try {
491 try {
492 bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
493 } catch (OutOfMemoryError e) {
494 // Accept failure, skip test
495 return;
496 }
497
498 long buf = alignUp(bufRaw, BASE_ALIGNMENT);
499
500 UNSAFE.copyMemory(null, buf, null, buf, size);
501 } catch (Exception e) {
502 throw new RuntimeException("copyMemory of large buffer failed");
503 } finally {
504 if (bufRaw != 0) {
505 UNSAFE.freeMemory(bufRaw);
506 }
507 }
508 }
509
510 /**
511 * Run positive tests
512 *
513 * @throws RuntimeException if an error is found
514 */
515 private void testPositive() {
516 testSmallCopy();
517 testLargeCopy();
518 }
519
520 /**
521 * Run negative tests, testing corner cases and the various exceptions
522 *
523 * @throws RuntimeException if an error is found
524 */
525 private void testNegative() {
526 long bufRaw = 0;
527
528 try {
529 bufRaw = UNSAFE.allocateMemory(1024);
530 long buf = alignUp(bufRaw, BASE_ALIGNMENT);
531 short[] arr = new short[16];
532
533 // Check illegal sizes
534 System.out.println("Testing negative size");
535 try {
536 UNSAFE.copyMemory(null, buf, null, buf, -1);
537 throw new RuntimeException("copyMemory failed to throw IAE for size=-1");
538 } catch (IllegalArgumentException e) {
539 // good
540 }
541
542 System.out.println("Testing negative srcOffset");
543 try {
544 // Check that negative srcOffset throws an IAE
545 UNSAFE.copyMemory(arr, -1, arr, UNSAFE.arrayBaseOffset(arr.getClass()), 16);
546 throw new RuntimeException("copyMemory failed to throw IAE for srcOffset=-1");
547 } catch (IllegalArgumentException e) {
548 // good
549 }
550
591 } finally {
592 if (bufRaw != 0) {
593 UNSAFE.freeMemory(bufRaw);
594 }
595 }
596 }
597
598 /**
599 * Run all tests
600 *
601 * @throws RuntimeException if an error is found
602 */
603 private void test() {
604 testPositive();
605 testNegative();
606 }
607
608 public static void main(String[] args) {
609 CopyMemory cs = new CopyMemory();
610 cs.test();
611 }
612
613 /**
614 * Helper class to represent a "pointer" - either a heap array or
615 * a pointer to a native buffer.
616 *
617 * In the case of a native pointer, the Object is null and the offset is
618 * the absolute address of the native buffer.
619 *
620 * In the case of a heap object, the Object is a primitive array, and
621 * the offset will be set to the base offset to the first element, meaning
622 * the object and the offset together form a double-register pointer.
623 */
624 static class GenericPointer {
625 private final Object o;
626 private final long offset;
627
628 private GenericPointer(Object o, long offset) {
629 this.o = o;
630 this.offset = offset;
631 }
632
633 public String toString() {
634 return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")";
635 }
636
637 public boolean equals(Object other) {
638 if (!(other instanceof GenericPointer)) {
639 return false;
640 }
641
642 GenericPointer otherp = (GenericPointer)other;
643
644 return o == otherp.o && offset == otherp.offset;
645 }
646
647 GenericPointer(Object o) {
648 this(o, UNSAFE.arrayBaseOffset(o.getClass()));
649 }
650
651 GenericPointer(long offset) {
652 this(null, offset);
653 }
654
655 public boolean isOnHeap() {
656 return o != null;
657 }
658
659 public Object getObject() {
660 return o;
661 }
662
663 public long getOffset() {
664 return offset;
665 }
666 }
667 }
|
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 * @test
29 * @summary Test Unsafe.copyMemory
30 * @modules java.base/jdk.internal.misc
31 */
32 public class CopyMemory {
33 private static final Unsafe UNSAFE;
34
35 static {
36 try {
37 Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
38 f.setAccessible(true);
39 UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
40 } catch (Exception e) {
41 throw new RuntimeException("Unable to get Unsafe instance.", e);
42 }
43 }
44
45 private CopyMemory() {
46 }
47
48 /**
49 * Run positive tests
50 *
51 * @throws RuntimeException if an error is found
52 */
53 private void testPositive() {
54 CopyCommon cc = new CopyCommon();
55 cc.testSmallCopy(false);
56 cc.testLargeCopy(false);
57 }
58
59 /**
60 * Run negative tests, testing corner cases and the various exceptions
61 *
62 * @throws RuntimeException if an error is found
63 */
64 private void testNegative() {
65 long bufRaw = 0;
66
67 try {
68 bufRaw = UNSAFE.allocateMemory(1024);
69 long buf = CopyCommon.alignUp(bufRaw, CopyCommon.BASE_ALIGNMENT);
70 short[] arr = new short[16];
71
72 // Check illegal sizes
73 System.out.println("Testing negative size");
74 try {
75 UNSAFE.copyMemory(null, buf, null, buf, -1);
76 throw new RuntimeException("copyMemory failed to throw IAE for size=-1");
77 } catch (IllegalArgumentException e) {
78 // good
79 }
80
81 System.out.println("Testing negative srcOffset");
82 try {
83 // Check that negative srcOffset throws an IAE
84 UNSAFE.copyMemory(arr, -1, arr, UNSAFE.arrayBaseOffset(arr.getClass()), 16);
85 throw new RuntimeException("copyMemory failed to throw IAE for srcOffset=-1");
86 } catch (IllegalArgumentException e) {
87 // good
88 }
89
130 } finally {
131 if (bufRaw != 0) {
132 UNSAFE.freeMemory(bufRaw);
133 }
134 }
135 }
136
137 /**
138 * Run all tests
139 *
140 * @throws RuntimeException if an error is found
141 */
142 private void test() {
143 testPositive();
144 testNegative();
145 }
146
147 public static void main(String[] args) {
148 CopyMemory cs = new CopyMemory();
149 cs.test();
150 }
151 }
|