1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package art; 18 19 import java.io.BufferedReader; 20 import java.io.File; 21 import java.io.FileReader; 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 import java.util.Collections; 25 import java.util.HashMap; 26 import java.util.HashSet; 27 import java.util.concurrent.CountDownLatch; 28 29 public class Test913 { run()30 public static void run() throws Exception { 31 doTest(); 32 33 // Use a countdown latch for synchronization, as join() will introduce more roots. 34 final CountDownLatch cdl1 = new CountDownLatch(1); 35 36 // Run the follow-references tests on a dedicated thread so we know the specific Thread type. 37 Thread t = new Thread() { 38 @Override 39 public void run() { 40 try { 41 Test913.runFollowReferences(); 42 } catch (Exception e) { 43 throw new RuntimeException(e); 44 } 45 cdl1.countDown(); 46 } 47 }; 48 t.start(); 49 cdl1.await(); 50 51 doExtensionTests(); 52 } 53 runFollowReferences()54 public static void runFollowReferences() throws Exception { 55 new TestConfig().doFollowReferencesTest(); 56 57 Runtime.getRuntime().gc(); 58 Runtime.getRuntime().gc(); 59 60 new TestConfig(null, 0, 1, -1).doFollowReferencesTest(); 61 62 Runtime.getRuntime().gc(); 63 Runtime.getRuntime().gc(); 64 65 new TestConfig(null, 0, Integer.MAX_VALUE, 1).doFollowReferencesTest(); 66 67 Runtime.getRuntime().gc(); 68 Runtime.getRuntime().gc(); 69 70 doStringTest(); 71 72 Runtime.getRuntime().gc(); 73 Runtime.getRuntime().gc(); 74 75 doPrimitiveArrayTest(); 76 doPrimitiveFieldTest(); 77 78 Runtime.getRuntime().gc(); 79 Runtime.getRuntime().gc(); 80 81 // Test klass filter. 82 System.out.println("--- klass ---"); 83 new TestConfig(A.class, 0).doFollowReferencesTest(); 84 85 // Test heap filter. 86 System.out.println("--- heap_filter ---"); 87 System.out.println("---- tagged objects"); 88 new TestConfig(null, 0x4).doFollowReferencesTest(); 89 System.out.println("---- untagged objects"); 90 new TestConfig(null, 0x8).doFollowReferencesTest(); 91 System.out.println("---- tagged classes"); 92 new TestConfig(null, 0x10).doFollowReferencesTest(); 93 System.out.println("---- untagged classes"); 94 new TestConfig(null, 0x20).doFollowReferencesTest(); 95 } 96 doTest()97 public static void doTest() throws Exception { 98 setupGcCallback(); 99 100 enableGcTracking(true); 101 runGc(); 102 enableGcTracking(false); 103 } 104 doStringTest()105 public static void doStringTest() throws Exception { 106 final String str = new String("HelloWorld"); 107 final String str2 = new String(""); 108 Object o = new Object() { 109 String s = str; 110 String s2 = str2; 111 }; 112 113 setTag(str, 1); 114 setTag(str2, 2); 115 System.out.println(Arrays.toString(followReferencesString(o))); 116 System.out.println(getTag(str)); 117 System.out.println(getTag(str2)); 118 } 119 doPrimitiveArrayTest()120 public static void doPrimitiveArrayTest() throws Exception { 121 final boolean[] zArray = new boolean[] { false, true }; 122 setTag(zArray, 1); 123 124 final byte[] bArray = new byte[] { 1, 2, 3 }; 125 setTag(bArray, 2); 126 127 final char[] cArray = new char[] { 'A', 'Z' }; 128 setTag(cArray, 3); 129 130 final short[] sArray = new short[] { 1, 2, 3 }; 131 setTag(sArray, 4); 132 133 final int[] iArray = new int[] { 1, 2, 3 }; 134 setTag(iArray, 5); 135 136 final float[] fArray = new float[] { 0.0f, 1.0f }; 137 setTag(fArray, 6); 138 139 final long[] lArray = new long[] { 1, 2, 3 }; 140 setTag(lArray, 7); 141 142 final double[] dArray = new double[] { 0.0, 1.0 }; 143 setTag(dArray, 8); 144 145 Object o = new Object() { 146 Object z = zArray; 147 Object b = bArray; 148 Object c = cArray; 149 Object s = sArray; 150 Object i = iArray; 151 Object f = fArray; 152 Object l = lArray; 153 Object d = dArray; 154 }; 155 156 System.out.println(followReferencesPrimitiveArray(o)); 157 System.out.print(getTag(zArray)); 158 System.out.print(getTag(bArray)); 159 System.out.print(getTag(cArray)); 160 System.out.print(getTag(sArray)); 161 System.out.print(getTag(iArray)); 162 System.out.print(getTag(fArray)); 163 System.out.print(getTag(lArray)); 164 System.out.println(getTag(dArray)); 165 } 166 doPrimitiveFieldTest()167 public static void doPrimitiveFieldTest() throws Exception { 168 // Force GCs to clean up dirt. 169 Runtime.getRuntime().gc(); 170 Runtime.getRuntime().gc(); 171 172 doTestPrimitiveFieldsClasses(); 173 174 doTestPrimitiveFieldsIntegral(); 175 176 // Force GCs to clean up dirt. 177 Runtime.getRuntime().gc(); 178 Runtime.getRuntime().gc(); 179 180 doTestPrimitiveFieldsFloat(); 181 182 // Force GCs to clean up dirt. 183 Runtime.getRuntime().gc(); 184 Runtime.getRuntime().gc(); 185 } 186 doTestPrimitiveFieldsClasses()187 private static void doTestPrimitiveFieldsClasses() { 188 setTag(IntObject.class, 10000); 189 System.out.println(followReferencesPrimitiveFields(IntObject.class)); 190 System.out.println(getTag(IntObject.class)); 191 setTag(IntObject.class, 0); 192 193 setTag(FloatObject.class, 10000); 194 System.out.println(followReferencesPrimitiveFields(FloatObject.class)); 195 System.out.println(getTag(FloatObject.class)); 196 setTag(FloatObject.class, 0); 197 198 setTag(Inf1.class, 10000); 199 System.out.println(followReferencesPrimitiveFields(Inf1.class)); 200 System.out.println(getTag(Inf1.class)); 201 setTag(Inf1.class, 0); 202 203 setTag(Inf2.class, 10000); 204 System.out.println(followReferencesPrimitiveFields(Inf2.class)); 205 System.out.println(getTag(Inf2.class)); 206 setTag(Inf2.class, 0); 207 } 208 doTestPrimitiveFieldsIntegral()209 private static void doTestPrimitiveFieldsIntegral() { 210 IntObject intObject = new IntObject(); 211 setTag(intObject, 10000); 212 System.out.println(followReferencesPrimitiveFields(intObject)); 213 System.out.println(getTag(intObject)); 214 } 215 doTestPrimitiveFieldsFloat()216 private static void doTestPrimitiveFieldsFloat() { 217 FloatObject floatObject = new FloatObject(); 218 setTag(floatObject, 10000); 219 System.out.println(followReferencesPrimitiveFields(floatObject)); 220 System.out.println(getTag(floatObject)); 221 } 222 223 static ArrayList<Object> extensionTestHolder; 224 doExtensionTests()225 private static void doExtensionTests() { 226 checkForExtensionApis(); 227 228 extensionTestHolder = new ArrayList<>(); 229 System.out.println(); 230 231 try { 232 getHeapName(-1); 233 System.out.println("Expected failure for -1"); 234 } catch (Exception e) { 235 } 236 System.out.println(getHeapName(0)); 237 System.out.println(getHeapName(1)); 238 System.out.println(getHeapName(2)); 239 System.out.println(getHeapName(3)); 240 try { 241 getHeapName(4); 242 System.out.println("Expected failure for -1"); 243 } catch (Exception e) { 244 } 245 246 System.out.println(); 247 248 setTag(Object.class, 100000); 249 int objectClassHeapId = getObjectHeapId(100000); 250 int objClassExpectedHeapId = hasImage() ? 1 : 3; 251 if (objectClassHeapId != objClassExpectedHeapId) { 252 throw new RuntimeException("Expected object class in heap " + objClassExpectedHeapId + 253 " but received " + objectClassHeapId); 254 } 255 256 A a = new A(); 257 extensionTestHolder.add(a); 258 setTag(a, 100001); 259 System.out.println(getObjectHeapId(100001)); 260 261 checkGetObjectHeapIdInCallback(100000, objClassExpectedHeapId); 262 checkGetObjectHeapIdInCallback(100001, 3); 263 264 long baseTag = 30000000; 265 setTag(Object.class, baseTag + objClassExpectedHeapId); 266 setTag(Class.class, baseTag + objClassExpectedHeapId); 267 Object o = new Object(); 268 extensionTestHolder.add(o); 269 setTag(o, baseTag + 3); 270 271 iterateThroughHeapExt(); 272 273 extensionTestHolder = null; 274 } 275 runGc()276 private static void runGc() { 277 clearStats(); 278 forceGarbageCollection(); 279 printStats(); 280 } 281 clearStats()282 private static void clearStats() { 283 getGcStarts(); 284 getGcFinishes(); 285 } 286 printStats()287 private static void printStats() { 288 System.out.println("---"); 289 int s = getGcStarts(); 290 int f = getGcFinishes(); 291 System.out.println((s > 0) + " " + (f > 0)); 292 } 293 hasImage()294 private static boolean hasImage() { 295 try { 296 int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); 297 BufferedReader reader = new BufferedReader(new FileReader("/proc/" + pid + "/maps")); 298 String line; 299 while ((line = reader.readLine()) != null) { 300 if (line.endsWith(".art")) { 301 reader.close(); 302 return true; 303 } 304 } 305 reader.close(); 306 return false; 307 } catch (Exception e) { 308 throw new RuntimeException(e); 309 } 310 } 311 312 private static class TestConfig { 313 private Class<?> klass = null; 314 private int heapFilter = 0; 315 private int stopAfter = Integer.MAX_VALUE; 316 private int followSet = -1; 317 TestConfig()318 public TestConfig() { 319 } TestConfig(Class<?> klass, int heapFilter)320 public TestConfig(Class<?> klass, int heapFilter) { 321 this.klass = klass; 322 this.heapFilter = heapFilter; 323 } TestConfig(Class<?> klass, int heapFilter, int stopAfter, int followSet)324 public TestConfig(Class<?> klass, int heapFilter, int stopAfter, int followSet) { 325 this.klass = klass; 326 this.heapFilter = heapFilter; 327 this.stopAfter = stopAfter; 328 this.followSet = followSet; 329 } 330 doFollowReferencesTest()331 public void doFollowReferencesTest() throws Exception { 332 // Force GCs to clean up dirt. 333 Runtime.getRuntime().gc(); 334 Runtime.getRuntime().gc(); 335 336 setTag(Thread.currentThread(), 3000); 337 338 { 339 ArrayList<Object> tmpStorage = new ArrayList<>(); 340 doFollowReferencesTestNonRoot(tmpStorage); 341 tmpStorage = null; 342 } 343 344 // Force GCs to clean up dirt. 345 Runtime.getRuntime().gc(); 346 Runtime.getRuntime().gc(); 347 348 doFollowReferencesTestRoot(); 349 350 // Force GCs to clean up dirt. 351 Runtime.getRuntime().gc(); 352 Runtime.getRuntime().gc(); 353 } 354 doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage)355 private void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) { 356 Verifier v = new Verifier(); 357 tagClasses(v); 358 A a = createTree(v); 359 tmpStorage.add(a); 360 v.add("0@0", "1@1000"); // tmpStorage[0] --(array-element)--> a. 361 362 doFollowReferencesTestImpl(null, stopAfter, followSet, null, v, null); 363 doFollowReferencesTestImpl(a.foo2, stopAfter, followSet, null, v, "3@1001"); 364 365 tmpStorage.clear(); 366 } 367 doFollowReferencesTestRoot()368 private void doFollowReferencesTestRoot() { 369 Verifier v = new Verifier(); 370 tagClasses(v); 371 A a = createTree(v); 372 373 doFollowReferencesTestImpl(null, stopAfter, followSet, a, v, null); 374 doFollowReferencesTestImpl(a.foo2, stopAfter, followSet, a, v, "3@1001"); 375 } 376 doFollowReferencesTestImpl(A root, int stopAfter, int followSet, Object asRoot, Verifier v, String additionalEnabled)377 private void doFollowReferencesTestImpl(A root, int stopAfter, int followSet, 378 Object asRoot, Verifier v, String additionalEnabled) { 379 String[] lines = 380 followReferences(heapFilter, klass, root, stopAfter, followSet, asRoot); 381 382 v.process(lines, additionalEnabled, heapFilter != 0 || klass != null); 383 } 384 tagClasses(Verifier v)385 private static void tagClasses(Verifier v) { 386 setTag(A.class, 1000); 387 388 setTag(B.class, 1001); 389 v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class. 390 391 setTag(C.class, 1002); 392 v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class. 393 v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class. 394 395 setTag(I1.class, 2000); 396 397 setTag(I2.class, 2001); 398 v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class. 399 } 400 createTree(Verifier v)401 private static A createTree(Verifier v) { 402 A aInst = new A(); 403 setTag(aInst, 1); 404 String aInstStr = "1@1000"; 405 String aClassStr = "1000@0"; 406 v.add(aInstStr, aClassStr); // A -->(class) --> A.class. 407 408 A a2Inst = new A(); 409 setTag(a2Inst, 2); 410 aInst.foo = a2Inst; 411 String a2InstStr = "2@1000"; 412 v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class. 413 v.add(aInstStr, a2InstStr); // A -->(field) --> A2. 414 415 B bInst = new B(); 416 setTag(bInst, 3); 417 aInst.foo2 = bInst; 418 String bInstStr = "3@1001"; 419 String bClassStr = "1001@0"; 420 v.add(bInstStr, bClassStr); // B -->(class) --> B.class. 421 v.add(aInstStr, bInstStr); // A -->(field) --> B. 422 423 A a3Inst = new A(); 424 setTag(a3Inst, 4); 425 bInst.bar = a3Inst; 426 String a3InstStr = "4@1000"; 427 v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class. 428 v.add(bInstStr, a3InstStr); // B -->(field) --> A3. 429 430 C cInst = new C(); 431 setTag(cInst, 5); 432 bInst.bar2 = cInst; 433 String cInstStr = "5@1000"; 434 String cClassStr = "1002@0"; 435 v.add(cInstStr, cClassStr); // C -->(class) --> C.class. 436 v.add(bInstStr, cInstStr); // B -->(field) --> C. 437 438 A a4Inst = new A(); 439 setTag(a4Inst, 6); 440 cInst.baz = a4Inst; 441 String a4InstStr = "6@1000"; 442 v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class. 443 v.add(cInstStr, a4InstStr); // C -->(field) --> A4. 444 445 cInst.baz2 = aInst; 446 v.add(cInstStr, aInstStr); // C -->(field) --> A. 447 448 A[] aArray = new A[2]; 449 setTag(aArray, 500); 450 aArray[1] = a2Inst; 451 cInst.array = aArray; 452 String aArrayStr = "500@0"; 453 v.add(cInstStr, aArrayStr); 454 v.add(aArrayStr, a2InstStr); 455 456 return aInst; 457 } 458 } 459 460 public static class A { 461 public A foo; 462 public A foo2; 463 A()464 public A() {} A(A a, A b)465 public A(A a, A b) { 466 foo = a; 467 foo2 = b; 468 } 469 } 470 471 public static class B extends A { 472 public A bar; 473 public A bar2; 474 B()475 public B() {} B(A a, A b)476 public B(A a, A b) { 477 bar = a; 478 bar2 = b; 479 } 480 } 481 482 public static interface I1 { 483 public final static int i1Field = 1; 484 } 485 486 public static interface I2 extends I1 { 487 public final static int i2Field = 2; 488 } 489 490 public static class C extends B implements I2 { 491 public A baz; 492 public A baz2; 493 public A[] array; 494 C()495 public C() {} C(A a, A b)496 public C(A a, A b) { 497 baz = a; 498 baz2 = b; 499 } 500 } 501 502 private static interface Inf1 { 503 public final static int A = 1; 504 } 505 506 private static interface Inf2 extends Inf1 { 507 public final static int B = 1; 508 } 509 510 private static class IntObject implements Inf1 { 511 byte b = (byte)1; 512 char c= 'a'; 513 short s = (short)2; 514 int i = 3; 515 long l = 4; 516 Object o = new Object(); 517 static int sI = 5; 518 } 519 520 private static class FloatObject extends IntObject implements Inf2 { 521 float f = 1.23f; 522 double d = 1.23; 523 Object p = new Object(); 524 static int sI = 6; 525 } 526 527 public static class Verifier { 528 // Should roots with vreg=-1 be printed? 529 public final static boolean PRINT_ROOTS_WITH_UNKNOWN_VREG = false; 530 531 public static class Node { 532 public String referrer; 533 534 public HashSet<String> referrees = new HashSet<>(); 535 Node(String r)536 public Node(String r) { 537 referrer = r; 538 } 539 isRoot()540 public boolean isRoot() { 541 return referrer.startsWith("root@"); 542 } 543 } 544 545 HashMap<String, Node> nodes = new HashMap<>(); 546 Verifier()547 public Verifier() { 548 } 549 add(String referrer, String referree)550 public void add(String referrer, String referree) { 551 if (!nodes.containsKey(referrer)) { 552 nodes.put(referrer, new Node(referrer)); 553 } 554 if (referree != null) { 555 nodes.get(referrer).referrees.add(referree); 556 } 557 } 558 process(String[] lines, String additionalEnabledReferrer, boolean filtered)559 public void process(String[] lines, String additionalEnabledReferrer, boolean filtered) { 560 // This method isn't optimal. The loops could be merged. However, it's more readable if 561 // the different parts are separated. 562 563 ArrayList<String> rootLines = new ArrayList<>(); 564 ArrayList<String> nonRootLines = new ArrayList<>(); 565 566 // Check for consecutive chunks of referrers. Also ensure roots come first. 567 { 568 String currentHead = null; 569 boolean rootsDone = false; 570 HashSet<String> completedReferrers = new HashSet<>(); 571 for (String l : lines) { 572 String referrer = getReferrer(l); 573 574 if (isRoot(referrer)) { 575 if (rootsDone) { 576 System.out.println("ERROR: Late root " + l); 577 print(lines); 578 return; 579 } 580 rootLines.add(l); 581 continue; 582 } 583 584 rootsDone = true; 585 586 if (currentHead == null) { 587 currentHead = referrer; 588 } else { 589 // Ignore 0@0, as it can happen at any time (as it stands for all other objects). 590 if (!currentHead.equals(referrer) && !referrer.equals("0@0")) { 591 completedReferrers.add(currentHead); 592 currentHead = referrer; 593 if (completedReferrers.contains(referrer)) { 594 System.out.println("Non-contiguous referrer " + l); 595 print(lines); 596 return; 597 } 598 } 599 } 600 nonRootLines.add(l); 601 } 602 } 603 604 // Sort (root order is not specified) and print the roots. 605 // TODO: What about extra roots? JNI and the interpreter seem to introduce those (though it 606 // isn't clear why a debuggable-AoT test doesn't have the same, at least for locals). 607 // For now, swallow duplicates, and resolve once we have the metadata for the roots. 608 { 609 Collections.sort(rootLines); 610 String lastRoot = null; 611 for (String l : rootLines) { 612 if (lastRoot != null && lastRoot.equals(l)) { 613 continue; 614 } 615 lastRoot = l; 616 if (!PRINT_ROOTS_WITH_UNKNOWN_VREG && l.indexOf("vreg=-1") > 0) { 617 continue; 618 } 619 System.out.println(l); 620 } 621 } 622 623 if (filtered) { 624 // If we aren't tracking dependencies, just sort the lines and print. 625 // TODO: As the verifier is currently using the output lines to track dependencies, we 626 // cannot verify that output is correct when parts of it are suppressed by filters. 627 // To correctly track this we need to take node information into account, and 628 // actually analyze the graph. 629 Collections.sort(nonRootLines); 630 for (String l : nonRootLines) { 631 System.out.println(l); 632 } 633 634 System.out.println("---"); 635 return; 636 } 637 638 // Iterate through the lines, keeping track of which referrers are visited, to ensure the 639 // order is acceptable. 640 HashSet<String> enabled = new HashSet<>(); 641 if (additionalEnabledReferrer != null) { 642 enabled.add(additionalEnabledReferrer); 643 } 644 // Always add "0@0". 645 enabled.add("0@0"); 646 647 for (String l : lines) { 648 String referrer = getReferrer(l); 649 String referree = getReferree(l); 650 if (isRoot(referrer)) { 651 // For a root src, just enable the referree. 652 enabled.add(referree); 653 } else { 654 // Check that the referrer is enabled (may be visited). 655 if (!enabled.contains(referrer)) { 656 System.out.println("Referrer " + referrer + " not enabled: " + l); 657 print(lines); 658 return; 659 } 660 enabled.add(referree); 661 } 662 } 663 664 // Now just sort the non-root lines and output them 665 Collections.sort(nonRootLines); 666 for (String l : nonRootLines) { 667 System.out.println(l); 668 } 669 670 System.out.println("---"); 671 } 672 isRoot(String ref)673 public static boolean isRoot(String ref) { 674 return ref.startsWith("root@"); 675 } 676 getReferrer(String line)677 private static String getReferrer(String line) { 678 int i = line.indexOf(" --"); 679 if (i <= 0) { 680 throw new IllegalArgumentException(line); 681 } 682 int j = line.indexOf(' '); 683 if (i != j) { 684 throw new IllegalArgumentException(line); 685 } 686 return line.substring(0, i); 687 } 688 getReferree(String line)689 private static String getReferree(String line) { 690 int i = line.indexOf("--> "); 691 if (i <= 0) { 692 throw new IllegalArgumentException(line); 693 } 694 int j = line.indexOf(' ', i + 4); 695 if (j < 0) { 696 throw new IllegalArgumentException(line); 697 } 698 return line.substring(i + 4, j); 699 } 700 print(String[] lines)701 private static void print(String[] lines) { 702 for (String l : lines) { 703 System.out.println(l); 704 } 705 } 706 } 707 setTag(Object o, long tag)708 private static void setTag(Object o, long tag) { 709 Main.setTag(o, tag); 710 } getTag(Object o)711 private static long getTag(Object o) { 712 return Main.getTag(o); 713 } 714 setupGcCallback()715 private static native void setupGcCallback(); enableGcTracking(boolean enable)716 private static native void enableGcTracking(boolean enable); getGcStarts()717 private static native int getGcStarts(); getGcFinishes()718 private static native int getGcFinishes(); forceGarbageCollection()719 private static native void forceGarbageCollection(); 720 checkForExtensionApis()721 private static native void checkForExtensionApis(); getObjectHeapId(long tag)722 private static native int getObjectHeapId(long tag); getHeapName(int heapId)723 private static native String getHeapName(int heapId); checkGetObjectHeapIdInCallback(long tag, int heapId)724 private static native void checkGetObjectHeapIdInCallback(long tag, int heapId); 725 followReferences(int heapFilter, Class<?> klassFilter, Object initialObject, int stopAfter, int followSet, Object jniRef)726 public static native String[] followReferences(int heapFilter, Class<?> klassFilter, 727 Object initialObject, int stopAfter, int followSet, Object jniRef); followReferencesString(Object initialObject)728 public static native String[] followReferencesString(Object initialObject); followReferencesPrimitiveArray(Object initialObject)729 public static native String followReferencesPrimitiveArray(Object initialObject); followReferencesPrimitiveFields(Object initialObject)730 public static native String followReferencesPrimitiveFields(Object initialObject); 731 iterateThroughHeapExt()732 private static native void iterateThroughHeapExt(); 733 } 734