Home | History | Annotate | Download | only in art
      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 {
     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 
     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 
     97   public static void doTest() throws Exception {
     98     setupGcCallback();
     99 
    100     enableGcTracking(true);
    101     runGc();
    102     enableGcTracking(false);
    103   }
    104 
    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 
    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 
    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 
    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     boolean correctHeapValue = false;
    199     setTag(Inf1.class, 10000);
    200     String heapTrace = followReferencesPrimitiveFields(Inf1.class);
    201 
    202     if (!checkInitialized(Inf1.class)) {
    203       correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000");
    204     } else {
    205       correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000001");
    206     }
    207 
    208     if (!correctHeapValue)
    209       System.out.println("Heap Trace for Inf1 is not as expected:\n" + heapTrace);
    210 
    211     System.out.println(getTag(Inf1.class));
    212     setTag(Inf1.class, 0);
    213 
    214     setTag(Inf2.class, 10000);
    215     heapTrace = followReferencesPrimitiveFields(Inf2.class);
    216 
    217     if (!checkInitialized(Inf2.class)) {
    218       correctHeapValue = heapTrace.equals("10000@0 (static, int, index=1) 0000000000000000");
    219     } else {
    220       correctHeapValue = heapTrace.equals("10000@0 (static, int, index=1) 0000000000000001");
    221     }
    222 
    223     if (!correctHeapValue)
    224       System.out.println("Heap Trace for Inf2 is not as expected:\n" + heapTrace);
    225     System.out.println(getTag(Inf2.class));
    226     setTag(Inf2.class, 0);
    227   }
    228 
    229   private static void doTestPrimitiveFieldsIntegral() {
    230     IntObject intObject = new IntObject();
    231     setTag(intObject, 10000);
    232     System.out.println(followReferencesPrimitiveFields(intObject));
    233     System.out.println(getTag(intObject));
    234   }
    235 
    236   private static void doTestPrimitiveFieldsFloat() {
    237     FloatObject floatObject = new FloatObject();
    238     setTag(floatObject, 10000);
    239     System.out.println(followReferencesPrimitiveFields(floatObject));
    240     System.out.println(getTag(floatObject));
    241   }
    242 
    243   static ArrayList<Object> extensionTestHolder;
    244 
    245   private static void doExtensionTests() {
    246     checkForExtensionApis();
    247 
    248     extensionTestHolder = new ArrayList<>();
    249     System.out.println();
    250 
    251     try {
    252       getHeapName(-1);
    253       System.out.println("Expected failure for -1");
    254     } catch (Exception e) {
    255     }
    256     System.out.println(getHeapName(0));
    257     System.out.println(getHeapName(1));
    258     System.out.println(getHeapName(2));
    259     System.out.println(getHeapName(3));
    260     try {
    261       getHeapName(4);
    262       System.out.println("Expected failure for -1");
    263     } catch (Exception e) {
    264     }
    265 
    266     System.out.println();
    267 
    268     setTag(Object.class, 100000);
    269     int objectClassHeapId = getObjectHeapId(100000);
    270     int objClassExpectedHeapId = hasImage() ? 1 : 3;
    271     if (objectClassHeapId != objClassExpectedHeapId) {
    272       throw new RuntimeException("Expected object class in heap " + objClassExpectedHeapId +
    273           " but received " + objectClassHeapId);
    274     }
    275 
    276     A a = new A();
    277     extensionTestHolder.add(a);
    278     setTag(a, 100001);
    279     System.out.println(getObjectHeapId(100001));
    280 
    281     checkGetObjectHeapIdInCallback(100000, objClassExpectedHeapId);
    282     checkGetObjectHeapIdInCallback(100001, 3);
    283 
    284     long baseTag = 30000000;
    285     setTag(Object.class, baseTag + objClassExpectedHeapId);
    286     setTag(Class.class, baseTag + objClassExpectedHeapId);
    287     Object o = new Object();
    288     extensionTestHolder.add(o);
    289     setTag(o, baseTag + 3);
    290 
    291     iterateThroughHeapExt();
    292 
    293     extensionTestHolder = null;
    294   }
    295 
    296   private static void runGc() {
    297     clearStats();
    298     forceGarbageCollection();
    299     printStats();
    300   }
    301 
    302   private static void clearStats() {
    303     getGcStarts();
    304     getGcFinishes();
    305   }
    306 
    307   private static void printStats() {
    308     System.out.println("---");
    309     int s = getGcStarts();
    310     int f = getGcFinishes();
    311     System.out.println((s > 0) + " " + (f > 0));
    312   }
    313 
    314   private static boolean hasImage() {
    315     try {
    316       int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
    317       BufferedReader reader = new BufferedReader(new FileReader("/proc/" + pid + "/maps"));
    318       String line;
    319       while ((line = reader.readLine()) != null) {
    320         if (line.endsWith(".art")) {
    321           reader.close();
    322           return true;
    323         }
    324       }
    325       reader.close();
    326       return false;
    327     } catch (Exception e) {
    328       throw new RuntimeException(e);
    329     }
    330   }
    331 
    332   private static class TestConfig {
    333     private Class<?> klass = null;
    334     private int heapFilter = 0;
    335     private int stopAfter = Integer.MAX_VALUE;
    336     private int followSet = -1;
    337 
    338     public TestConfig() {
    339     }
    340     public TestConfig(Class<?> klass, int heapFilter) {
    341       this.klass = klass;
    342       this.heapFilter = heapFilter;
    343     }
    344     public TestConfig(Class<?> klass, int heapFilter, int stopAfter, int followSet) {
    345       this.klass = klass;
    346       this.heapFilter = heapFilter;
    347       this.stopAfter = stopAfter;
    348       this.followSet = followSet;
    349     }
    350 
    351     public void doFollowReferencesTest() throws Exception {
    352       // Force GCs to clean up dirt.
    353       Runtime.getRuntime().gc();
    354       Runtime.getRuntime().gc();
    355 
    356       setTag(Thread.currentThread(), 3000);
    357 
    358       {
    359         ArrayList<Object> tmpStorage = new ArrayList<>();
    360         doFollowReferencesTestNonRoot(tmpStorage);
    361         tmpStorage = null;
    362       }
    363 
    364       // Force GCs to clean up dirt.
    365       Runtime.getRuntime().gc();
    366       Runtime.getRuntime().gc();
    367 
    368       doFollowReferencesTestRoot();
    369 
    370       // Force GCs to clean up dirt.
    371       Runtime.getRuntime().gc();
    372       Runtime.getRuntime().gc();
    373     }
    374 
    375     private void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) {
    376       Verifier v = new Verifier();
    377       tagClasses(v);
    378       A a = createTree(v);
    379       tmpStorage.add(a);
    380       v.add("0@0", "1@1000");  // tmpStorage[0] --(array-element)--> a.
    381 
    382       doFollowReferencesTestImpl(null, stopAfter, followSet, null, v, null);
    383       doFollowReferencesTestImpl(a.foo2, stopAfter, followSet, null, v, "3@1001");
    384 
    385       tmpStorage.clear();
    386     }
    387 
    388     private void doFollowReferencesTestRoot() {
    389       Verifier v = new Verifier();
    390       tagClasses(v);
    391       A a = createTree(v);
    392 
    393       doFollowReferencesTestImpl(null, stopAfter, followSet, a, v, null);
    394       doFollowReferencesTestImpl(a.foo2, stopAfter, followSet, a, v, "3@1001");
    395     }
    396 
    397     private void doFollowReferencesTestImpl(A root, int stopAfter, int followSet,
    398         Object asRoot, Verifier v, String additionalEnabled) {
    399       String[] lines =
    400           followReferences(heapFilter, klass, root, stopAfter, followSet, asRoot);
    401 
    402       v.process(lines, additionalEnabled, heapFilter != 0 || klass != null);
    403     }
    404 
    405     private static void tagClasses(Verifier v) {
    406       setTag(A.class, 1000);
    407       registerClass(1000, A.class);
    408 
    409       setTag(B.class, 1001);
    410       registerClass(1001, B.class);
    411       v.add("1001@0", "1000@0");  // B.class --(superclass)--> A.class.
    412 
    413       setTag(C.class, 1002);
    414       registerClass(1002, C.class);
    415       v.add("1002@0", "1001@0");  // C.class --(superclass)--> B.class.
    416       v.add("1002@0", "2001@0");  // C.class --(interface)--> I2.class.
    417 
    418       setTag(I1.class, 2000);
    419       registerClass(2000, I1.class);
    420 
    421       setTag(I2.class, 2001);
    422       registerClass(2001, I2.class);
    423       v.add("2001@0", "2000@0");  // I2.class --(interface)--> I1.class.
    424     }
    425 
    426     private static A createTree(Verifier v) {
    427       A aInst = new A();
    428       setTag(aInst, 1);
    429       String aInstStr = "1@1000";
    430       String aClassStr = "1000@0";
    431       v.add(aInstStr, aClassStr);  // A -->(class) --> A.class.
    432 
    433       A a2Inst = new A();
    434       setTag(a2Inst, 2);
    435       aInst.foo = a2Inst;
    436       String a2InstStr = "2@1000";
    437       v.add(a2InstStr, aClassStr);  // A2 -->(class) --> A.class.
    438       v.add(aInstStr, a2InstStr);   // A -->(field) --> A2.
    439 
    440       B bInst = new B();
    441       setTag(bInst, 3);
    442       aInst.foo2 = bInst;
    443       String bInstStr = "3@1001";
    444       String bClassStr = "1001@0";
    445       v.add(bInstStr, bClassStr);  // B -->(class) --> B.class.
    446       v.add(aInstStr, bInstStr);   // A -->(field) --> B.
    447 
    448       A a3Inst = new A();
    449       setTag(a3Inst, 4);
    450       bInst.bar = a3Inst;
    451       String a3InstStr = "4@1000";
    452       v.add(a3InstStr, aClassStr);  // A3 -->(class) --> A.class.
    453       v.add(bInstStr, a3InstStr);   // B -->(field) --> A3.
    454 
    455       C cInst = new C();
    456       setTag(cInst, 5);
    457       bInst.bar2 = cInst;
    458       String cInstStr = "5@1000";
    459       String cClassStr = "1002@0";
    460       v.add(cInstStr, cClassStr);  // C -->(class) --> C.class.
    461       v.add(bInstStr, cInstStr);   // B -->(field) --> C.
    462 
    463       A a4Inst = new A();
    464       setTag(a4Inst, 6);
    465       cInst.baz = a4Inst;
    466       String a4InstStr = "6@1000";
    467       v.add(a4InstStr, aClassStr);  // A4 -->(class) --> A.class.
    468       v.add(cInstStr, a4InstStr);   // C -->(field) --> A4.
    469 
    470       cInst.baz2 = aInst;
    471       v.add(cInstStr, aInstStr);  // C -->(field) --> A.
    472 
    473       A[] aArray = new A[2];
    474       setTag(aArray, 500);
    475       aArray[1] = a2Inst;
    476       cInst.array = aArray;
    477       String aArrayStr = "500@0";
    478       v.add(cInstStr, aArrayStr);
    479       v.add(aArrayStr, a2InstStr);
    480 
    481       return aInst;
    482     }
    483   }
    484 
    485   public static class A {
    486     public A foo;
    487     public A foo2;
    488 
    489     public A() {}
    490     public A(A a, A b) {
    491       foo = a;
    492       foo2 = b;
    493     }
    494   }
    495 
    496   public static class B extends A {
    497     public A bar;
    498     public A bar2;
    499 
    500     public B() {}
    501     public B(A a, A b) {
    502       bar = a;
    503       bar2 = b;
    504     }
    505   }
    506 
    507   public static interface I1 {
    508     public final static int i1Field = 1;
    509   }
    510 
    511   public static interface I2 extends I1 {
    512     public final static int i2Field = 2;
    513   }
    514 
    515   public static class C extends B implements I2 {
    516     public A baz;
    517     public A baz2;
    518     public A[] array;
    519 
    520     public C() {}
    521     public C(A a, A b) {
    522       baz = a;
    523       baz2 = b;
    524     }
    525   }
    526 
    527   private static interface Inf1 {
    528     public final static int A = 1;
    529   }
    530 
    531   private static interface Inf2 extends Inf1 {
    532     public final static int B = 1;
    533   }
    534 
    535   private static class IntObject implements Inf1 {
    536     byte b = (byte)1;
    537     char c= 'a';
    538     short s = (short)2;
    539     int i = 3;
    540     long l = 4;
    541     Object o = new Object();
    542     static int sI = 5;
    543   }
    544 
    545   private static class FloatObject extends IntObject implements Inf2 {
    546     float f = 1.23f;
    547     double d = 1.23;
    548     Object p = new Object();
    549     static int sI = 6;
    550   }
    551 
    552   public static class Verifier {
    553     // Should roots with vreg=-1 be printed?
    554     public final static boolean PRINT_ROOTS_WITH_UNKNOWN_VREG = false;
    555 
    556     public static class Node {
    557       public String referrer;
    558 
    559       public HashSet<String> referrees = new HashSet<>();
    560 
    561       public Node(String r) {
    562         referrer = r;
    563       }
    564 
    565       public boolean isRoot() {
    566         return referrer.startsWith("root@");
    567       }
    568     }
    569 
    570     HashMap<String, Node> nodes = new HashMap<>();
    571 
    572     public Verifier() {
    573     }
    574 
    575     public void add(String referrer, String referree) {
    576       if (!nodes.containsKey(referrer)) {
    577         nodes.put(referrer, new Node(referrer));
    578       }
    579       if (referree != null) {
    580         nodes.get(referrer).referrees.add(referree);
    581       }
    582     }
    583 
    584     public void process(String[] lines, String additionalEnabledReferrer, boolean filtered) {
    585       // This method isn't optimal. The loops could be merged. However, it's more readable if
    586       // the different parts are separated.
    587 
    588       ArrayList<String> rootLines = new ArrayList<>();
    589       ArrayList<String> nonRootLines = new ArrayList<>();
    590 
    591       // Check for consecutive chunks of referrers. Also ensure roots come first.
    592       {
    593         String currentHead = null;
    594         boolean rootsDone = false;
    595         HashSet<String> completedReferrers = new HashSet<>();
    596         for (String l : lines) {
    597           String referrer = getReferrer(l);
    598 
    599           if (isRoot(referrer)) {
    600             if (rootsDone) {
    601               System.out.println("ERROR: Late root " + l);
    602               print(lines);
    603               return;
    604             }
    605             rootLines.add(l);
    606             continue;
    607           }
    608 
    609           rootsDone = true;
    610 
    611           if (currentHead == null) {
    612             currentHead = referrer;
    613           } else {
    614             // Ignore 0@0, as it can happen at any time (as it stands for all other objects).
    615             if (!currentHead.equals(referrer) && !referrer.equals("0@0")) {
    616               completedReferrers.add(currentHead);
    617               currentHead = referrer;
    618               if (completedReferrers.contains(referrer)) {
    619                 System.out.println("Non-contiguous referrer " + l);
    620                 print(lines);
    621                 return;
    622               }
    623             }
    624           }
    625           nonRootLines.add(l);
    626         }
    627       }
    628 
    629       // Sort (root order is not specified) and print the roots.
    630       // TODO: What about extra roots? JNI and the interpreter seem to introduce those (though it
    631       //       isn't clear why a debuggable-AoT test doesn't have the same, at least for locals).
    632       //       For now, swallow duplicates, and resolve once we have the metadata for the roots.
    633       {
    634         Collections.sort(rootLines);
    635         String lastRoot = null;
    636         for (String l : rootLines) {
    637           if (lastRoot != null && lastRoot.equals(l)) {
    638             continue;
    639           }
    640           lastRoot = l;
    641           if (!PRINT_ROOTS_WITH_UNKNOWN_VREG && l.indexOf("vreg=-1") > 0) {
    642             continue;
    643           }
    644           System.out.println(l);
    645         }
    646       }
    647 
    648       if (filtered) {
    649         // If we aren't tracking dependencies, just sort the lines and print.
    650         // TODO: As the verifier is currently using the output lines to track dependencies, we
    651         //       cannot verify that output is correct when parts of it are suppressed by filters.
    652         //       To correctly track this we need to take node information into account, and
    653         //       actually analyze the graph.
    654         Collections.sort(nonRootLines);
    655         for (String l : nonRootLines) {
    656           System.out.println(l);
    657         }
    658 
    659         System.out.println("---");
    660         return;
    661       }
    662 
    663       // Iterate through the lines, keeping track of which referrers are visited, to ensure the
    664       // order is acceptable.
    665       HashSet<String> enabled = new HashSet<>();
    666       if (additionalEnabledReferrer != null) {
    667         enabled.add(additionalEnabledReferrer);
    668       }
    669       // Always add "0@0".
    670       enabled.add("0@0");
    671 
    672       for (String l : lines) {
    673         String referrer = getReferrer(l);
    674         String referree = getReferree(l);
    675         if (isRoot(referrer)) {
    676           // For a root src, just enable the referree.
    677           enabled.add(referree);
    678         } else {
    679           // Check that the referrer is enabled (may be visited).
    680           if (!enabled.contains(referrer)) {
    681             System.out.println("Referrer " + referrer + " not enabled: " + l);
    682             print(lines);
    683             return;
    684           }
    685           enabled.add(referree);
    686         }
    687       }
    688 
    689       // Now just sort the non-root lines and output them
    690       Collections.sort(nonRootLines);
    691       for (String l : nonRootLines) {
    692         System.out.println(l);
    693       }
    694 
    695       System.out.println("---");
    696     }
    697 
    698     public static boolean isRoot(String ref) {
    699       return ref.startsWith("root@");
    700     }
    701 
    702     private static String getReferrer(String line) {
    703       int i = line.indexOf(" --");
    704       if (i <= 0) {
    705         throw new IllegalArgumentException(line);
    706       }
    707       int j = line.indexOf(' ');
    708       if (i != j) {
    709         throw new IllegalArgumentException(line);
    710       }
    711       return line.substring(0, i);
    712     }
    713 
    714     private static String getReferree(String line) {
    715       int i = line.indexOf("--> ");
    716       if (i <= 0) {
    717         throw new IllegalArgumentException(line);
    718       }
    719       int j = line.indexOf(' ', i + 4);
    720       if (j < 0) {
    721         throw new IllegalArgumentException(line);
    722       }
    723       return line.substring(i + 4, j);
    724     }
    725 
    726     private static void print(String[] lines) {
    727       for (String l : lines) {
    728         System.out.println(l);
    729       }
    730     }
    731   }
    732 
    733   private static void setTag(Object o, long tag) {
    734     Main.setTag(o, tag);
    735   }
    736   private static long getTag(Object o) {
    737     return Main.getTag(o);
    738   }
    739 
    740   private static native boolean checkInitialized(Class<?> klass);
    741   private static native void setupGcCallback();
    742   private static native void enableGcTracking(boolean enable);
    743   private static native int getGcStarts();
    744   private static native int getGcFinishes();
    745   private static native void forceGarbageCollection();
    746 
    747   private static native void checkForExtensionApis();
    748   private static native int getObjectHeapId(long tag);
    749   private static native String getHeapName(int heapId);
    750   private static native void checkGetObjectHeapIdInCallback(long tag, int heapId);
    751 
    752   public static native String[] followReferences(int heapFilter, Class<?> klassFilter,
    753       Object initialObject, int stopAfter, int followSet, Object jniRef);
    754   public static native String[] followReferencesString(Object initialObject);
    755   public static native String followReferencesPrimitiveArray(Object initialObject);
    756   public static native String followReferencesPrimitiveFields(Object initialObject);
    757 
    758   private static native void iterateThroughHeapExt();
    759 
    760   private static native void registerClass(long tag, Object obj);
    761 }
    762