blob: fc00ada9168014bba4a03a839148c58b70dd7bb3 [file] [log] [blame]
Andreas Gampe8da6d032016-10-31 19:31:03 -07001/*
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
17import java.util.ArrayList;
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070018import java.util.Collections;
Andreas Gampe5d139fc2016-11-09 22:54:25 -080019import java.util.HashMap;
20import java.util.HashSet;
Andreas Gampe8da6d032016-10-31 19:31:03 -070021
22public class Main {
23 public static void main(String[] args) throws Exception {
24 System.loadLibrary(args[1]);
25
26 doTest();
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070027 doFollowReferencesTest();
Andreas Gampe8da6d032016-10-31 19:31:03 -070028 }
29
30 public static void doTest() throws Exception {
31 setupGcCallback();
32
33 enableGcTracking(true);
34 run();
35 enableGcTracking(false);
36 }
37
38 private static void run() {
Andreas Gampeef3ace02016-11-01 13:58:14 -070039 clearStats();
Andreas Gampe8da6d032016-10-31 19:31:03 -070040 forceGarbageCollection();
41 printStats();
42 }
43
Andreas Gampeef3ace02016-11-01 13:58:14 -070044 private static void clearStats() {
45 getGcStarts();
46 getGcFinishes();
47 }
48
Andreas Gampe8da6d032016-10-31 19:31:03 -070049 private static void printStats() {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070050 System.out.println("---");
51 int s = getGcStarts();
52 int f = getGcFinishes();
53 System.out.println((s > 0) + " " + (f > 0));
54 }
55
56 public static void doFollowReferencesTest() throws Exception {
57 // Force GCs to clean up dirt.
58 Runtime.getRuntime().gc();
59 Runtime.getRuntime().gc();
60
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070061 setTag(Thread.currentThread(), 3000);
62
63 {
64 ArrayList<Object> tmpStorage = new ArrayList<>();
65 doFollowReferencesTestNonRoot(tmpStorage);
66 tmpStorage = null;
67 }
68
69 // Force GCs to clean up dirt.
70 Runtime.getRuntime().gc();
71 Runtime.getRuntime().gc();
72
73 doFollowReferencesTestRoot();
74
75 // Force GCs to clean up dirt.
76 Runtime.getRuntime().gc();
77 Runtime.getRuntime().gc();
78 }
79
80 private static void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) {
Andreas Gampe5d139fc2016-11-09 22:54:25 -080081 Verifier v = new Verifier();
82 tagClasses(v);
83 A a = createTree(v);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070084 tmpStorage.add(a);
Andreas Gampe5d139fc2016-11-09 22:54:25 -080085 v.add("0@0", "1@1000"); // tmpStorage[0] --(array-element)--> a.
86
87 doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null, v, null);
88 doFollowReferencesTestImpl(a.foo, Integer.MAX_VALUE, -1, null, v, "2@1000");
89
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070090 tmpStorage.clear();
91 }
92
93 private static void doFollowReferencesTestRoot() {
Andreas Gampe5d139fc2016-11-09 22:54:25 -080094 Verifier v = new Verifier();
95 tagClasses(v);
96 A a = createTree(v);
97
98 doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a, v, null);
99 doFollowReferencesTestImpl(a.foo, Integer.MAX_VALUE, -1, a, v, "2@1000");
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700100 }
101
102 private static void doFollowReferencesTestImpl(A root, int stopAfter, int followSet,
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800103 Object asRoot, Verifier v, String additionalEnabled) {
104 waitForJitCompilation(); // Wait to avoid JIT influence (e.g., JNI globals).
105
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700106 String[] lines =
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800107 followReferences(0, null, root, stopAfter, followSet, asRoot);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700108
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800109 v.process(lines, additionalEnabled);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700110
111 // TODO: Test filters.
112 }
113
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800114 private static void tagClasses(Verifier v) {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700115 setTag(A.class, 1000);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800116
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700117 setTag(B.class, 1001);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800118 v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class.
119
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700120 setTag(C.class, 1002);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800121 v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class.
122 v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class.
123
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700124 setTag(I1.class, 2000);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800125
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700126 setTag(I2.class, 2001);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800127 v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700128 }
129
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800130 private static A createTree(Verifier v) {
131 A aInst = new A();
132 setTag(aInst, 1);
133 String aInstStr = "1@1000";
134 String aClassStr = "1000@0";
135 v.add(aInstStr, aClassStr); // A -->(class) --> A.class.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700136
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800137 A a2Inst = new A();
138 setTag(a2Inst, 2);
139 aInst.foo = a2Inst;
140 String a2InstStr = "2@1000";
141 v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class.
142 v.add(aInstStr, a2InstStr); // A -->(field) --> A2.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700143
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800144 B bInst = new B();
145 setTag(bInst, 3);
146 aInst.foo2 = bInst;
147 String bInstStr = "3@1001";
148 String bClassStr = "1001@0";
149 v.add(bInstStr, bClassStr); // B -->(class) --> B.class.
150 v.add(aInstStr, bInstStr); // A -->(field) --> B.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700151
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800152 A a3Inst = new A();
153 setTag(a3Inst, 4);
154 bInst.bar = a3Inst;
155 String a3InstStr = "4@1000";
156 v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class.
157 v.add(bInstStr, a3InstStr); // B -->(field) --> A3.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700158
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800159 C cInst = new C();
160 setTag(cInst, 5);
161 bInst.bar2 = cInst;
162 String cInstStr = "5@1000";
163 String cClassStr = "1002@0";
164 v.add(cInstStr, cClassStr); // C -->(class) --> C.class.
165 v.add(bInstStr, cInstStr); // B -->(field) --> C.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700166
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800167 A a4Inst = new A();
168 setTag(a4Inst, 6);
169 cInst.baz = a4Inst;
170 String a4InstStr = "6@1000";
171 v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class.
172 v.add(cInstStr, a4InstStr); // C -->(field) --> A4.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700173
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800174 cInst.baz2 = aInst;
175 v.add(cInstStr, aInstStr); // C -->(field) --> A.
176
177 return aInst;
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700178 }
179
180 public static class A {
181 public A foo;
182 public A foo2;
183
184 public A() {}
185 public A(A a, A b) {
186 foo = a;
187 foo2 = b;
188 }
189 }
190
191 public static class B extends A {
192 public A bar;
193 public A bar2;
194
195 public B() {}
196 public B(A a, A b) {
197 bar = a;
198 bar2 = b;
199 }
200 }
201
202 public static interface I1 {
203 public final static int i1Field = 1;
204 }
205
206 public static interface I2 extends I1 {
207 public final static int i2Field = 2;
208 }
209
210 public static class C extends B implements I2 {
211 public A baz;
212 public A baz2;
213
214 public C() {}
215 public C(A a, A b) {
216 baz = a;
217 baz2 = b;
218 }
Andreas Gampe8da6d032016-10-31 19:31:03 -0700219 }
220
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800221 public static class Verifier {
222 public static class Node {
223 public String referrer;
224
225 public HashSet<String> referrees = new HashSet<>();
226
227 public Node(String r) {
228 referrer = r;
229 }
230
231 public boolean isRoot() {
232 return referrer.startsWith("root@");
233 }
234 }
235
236 HashMap<String, Node> nodes = new HashMap<>();
237
238 public Verifier() {
239 }
240
241 public void add(String referrer, String referree) {
242 if (!nodes.containsKey(referrer)) {
243 nodes.put(referrer, new Node(referrer));
244 }
245 if (referree != null) {
246 nodes.get(referrer).referrees.add(referree);
247 }
248 }
249
250 public void process(String[] lines, String additionalEnabledReferrer) {
251 // This method isn't optimal. The loops could be merged. However, it's more readable if
252 // the different parts are separated.
253
254 ArrayList<String> rootLines = new ArrayList<>();
255 ArrayList<String> nonRootLines = new ArrayList<>();
256
257 // Check for consecutive chunks of referrers. Also ensure roots come first.
258 {
259 String currentHead = null;
260 boolean rootsDone = false;
261 HashSet<String> completedReferrers = new HashSet<>();
262 for (String l : lines) {
263 String referrer = getReferrer(l);
264
265 if (isRoot(referrer)) {
266 if (rootsDone) {
267 System.out.println("ERROR: Late root " + l);
268 print(lines);
269 return;
270 }
271 rootLines.add(l);
272 continue;
273 }
274
275 rootsDone = true;
276
277 if (currentHead == null) {
278 currentHead = referrer;
279 } else {
280 if (!currentHead.equals(referrer)) {
281 completedReferrers.add(currentHead);
282 currentHead = referrer;
283 if (completedReferrers.contains(referrer)) {
284 System.out.println("Non-contiguous referrer " + l);
285 print(lines);
286 return;
287 }
288 }
289 }
290 nonRootLines.add(l);
291 }
292 }
293
294 // Sort (root order is not specified) and print the roots.
295 // TODO: What about extra roots? JNI and the interpreter seem to introduce those (though it
296 // isn't clear why a debuggable-AoT test doesn't have the same, at least for locals).
297 // For now, swallow duplicates, and resolve once we have the metadata for the roots.
298 {
299 Collections.sort(rootLines);
300 String lastRoot = null;
301 for (String l : rootLines) {
302 if (lastRoot != null && lastRoot.equals(l)) {
303 continue;
304 }
305 lastRoot = l;
306 System.out.println(l);
307 }
308 }
309
310 // Iterate through the lines, keeping track of which referrers are visited, to ensure the
311 // order is acceptable.
312 HashSet<String> enabled = new HashSet<>();
313 if (additionalEnabledReferrer != null) {
314 enabled.add(additionalEnabledReferrer);
315 }
316 // Always add "0@0".
317 enabled.add("0@0");
318
319 for (String l : lines) {
320 String referrer = getReferrer(l);
321 String referree = getReferree(l);
322 if (isRoot(referrer)) {
323 // For a root src, just enable the referree.
324 enabled.add(referree);
325 } else {
326 // Check that the referrer is enabled (may be visited).
327 if (!enabled.contains(referrer)) {
328 System.out.println("Referrer " + referrer + " not enabled: " + l);
329 print(lines);
330 return;
331 }
332 enabled.add(referree);
333 }
334 }
335
336 // Now just sort the non-root lines and output them
337 Collections.sort(nonRootLines);
338 for (String l : nonRootLines) {
339 System.out.println(l);
340 }
341
342 System.out.println("---");
343 }
344
345 public static boolean isRoot(String ref) {
346 return ref.startsWith("root@");
347 }
348
349 private static String getReferrer(String line) {
350 int i = line.indexOf(" --");
351 if (i <= 0) {
352 throw new IllegalArgumentException(line);
353 }
354 int j = line.indexOf(' ');
355 if (i != j) {
356 throw new IllegalArgumentException(line);
357 }
358 return line.substring(0, i);
359 }
360
361 private static String getReferree(String line) {
362 int i = line.indexOf("--> ");
363 if (i <= 0) {
364 throw new IllegalArgumentException(line);
365 }
366 int j = line.indexOf(' ', i + 4);
367 if (j < 0) {
368 throw new IllegalArgumentException(line);
369 }
370 return line.substring(i + 4, j);
371 }
372
373 private static void print(String[] lines) {
374 for (String l : lines) {
375 System.out.println(l);
376 }
377 }
378 }
379
Andreas Gampe8da6d032016-10-31 19:31:03 -0700380 private static native void setupGcCallback();
381 private static native void enableGcTracking(boolean enable);
382 private static native int getGcStarts();
383 private static native int getGcFinishes();
384 private static native void forceGarbageCollection();
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700385
386 private static native void setTag(Object o, long tag);
387 private static native long getTag(Object o);
388
389 private static native String[] followReferences(int heapFilter, Class<?> klassFilter,
390 Object initialObject, int stopAfter, int followSet, Object jniRef);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800391
392 private static native void waitForJitCompilation();
Andreas Gampe8da6d032016-10-31 19:31:03 -0700393}