blob: 564596e02bd8c82fceda3aeea2625343c6370f37 [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);
Andreas Gampe638a6932016-12-02 19:11:17 -080088 doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, null, v, "3@1001");
Andreas Gampe5d139fc2016-11-09 22:54:25 -080089
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);
Andreas Gampe638a6932016-12-02 19:11:17 -080099 doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, a, v, "3@1001");
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) {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700104 String[] lines =
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800105 followReferences(0, null, root, stopAfter, followSet, asRoot);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700106
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800107 v.process(lines, additionalEnabled);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700108
109 // TODO: Test filters.
110 }
111
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800112 private static void tagClasses(Verifier v) {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700113 setTag(A.class, 1000);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800114
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700115 setTag(B.class, 1001);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800116 v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class.
117
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700118 setTag(C.class, 1002);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800119 v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class.
120 v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class.
121
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700122 setTag(I1.class, 2000);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800123
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700124 setTag(I2.class, 2001);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800125 v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700126 }
127
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800128 private static A createTree(Verifier v) {
129 A aInst = new A();
130 setTag(aInst, 1);
131 String aInstStr = "1@1000";
132 String aClassStr = "1000@0";
133 v.add(aInstStr, aClassStr); // A -->(class) --> A.class.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700134
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800135 A a2Inst = new A();
136 setTag(a2Inst, 2);
137 aInst.foo = a2Inst;
138 String a2InstStr = "2@1000";
139 v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class.
140 v.add(aInstStr, a2InstStr); // A -->(field) --> A2.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700141
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800142 B bInst = new B();
143 setTag(bInst, 3);
144 aInst.foo2 = bInst;
145 String bInstStr = "3@1001";
146 String bClassStr = "1001@0";
147 v.add(bInstStr, bClassStr); // B -->(class) --> B.class.
148 v.add(aInstStr, bInstStr); // A -->(field) --> B.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700149
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800150 A a3Inst = new A();
151 setTag(a3Inst, 4);
152 bInst.bar = a3Inst;
153 String a3InstStr = "4@1000";
154 v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class.
155 v.add(bInstStr, a3InstStr); // B -->(field) --> A3.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700156
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800157 C cInst = new C();
158 setTag(cInst, 5);
159 bInst.bar2 = cInst;
160 String cInstStr = "5@1000";
161 String cClassStr = "1002@0";
162 v.add(cInstStr, cClassStr); // C -->(class) --> C.class.
163 v.add(bInstStr, cInstStr); // B -->(field) --> C.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700164
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800165 A a4Inst = new A();
166 setTag(a4Inst, 6);
167 cInst.baz = a4Inst;
168 String a4InstStr = "6@1000";
169 v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class.
170 v.add(cInstStr, a4InstStr); // C -->(field) --> A4.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700171
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800172 cInst.baz2 = aInst;
173 v.add(cInstStr, aInstStr); // C -->(field) --> A.
174
175 return aInst;
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700176 }
177
178 public static class A {
179 public A foo;
180 public A foo2;
181
182 public A() {}
183 public A(A a, A b) {
184 foo = a;
185 foo2 = b;
186 }
187 }
188
189 public static class B extends A {
190 public A bar;
191 public A bar2;
192
193 public B() {}
194 public B(A a, A b) {
195 bar = a;
196 bar2 = b;
197 }
198 }
199
200 public static interface I1 {
201 public final static int i1Field = 1;
202 }
203
204 public static interface I2 extends I1 {
205 public final static int i2Field = 2;
206 }
207
208 public static class C extends B implements I2 {
209 public A baz;
210 public A baz2;
211
212 public C() {}
213 public C(A a, A b) {
214 baz = a;
215 baz2 = b;
216 }
Andreas Gampe8da6d032016-10-31 19:31:03 -0700217 }
218
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800219 public static class Verifier {
220 public static class Node {
221 public String referrer;
222
223 public HashSet<String> referrees = new HashSet<>();
224
225 public Node(String r) {
226 referrer = r;
227 }
228
229 public boolean isRoot() {
230 return referrer.startsWith("root@");
231 }
232 }
233
234 HashMap<String, Node> nodes = new HashMap<>();
235
236 public Verifier() {
237 }
238
239 public void add(String referrer, String referree) {
240 if (!nodes.containsKey(referrer)) {
241 nodes.put(referrer, new Node(referrer));
242 }
243 if (referree != null) {
244 nodes.get(referrer).referrees.add(referree);
245 }
246 }
247
248 public void process(String[] lines, String additionalEnabledReferrer) {
249 // This method isn't optimal. The loops could be merged. However, it's more readable if
250 // the different parts are separated.
251
252 ArrayList<String> rootLines = new ArrayList<>();
253 ArrayList<String> nonRootLines = new ArrayList<>();
254
255 // Check for consecutive chunks of referrers. Also ensure roots come first.
256 {
257 String currentHead = null;
258 boolean rootsDone = false;
259 HashSet<String> completedReferrers = new HashSet<>();
260 for (String l : lines) {
261 String referrer = getReferrer(l);
262
263 if (isRoot(referrer)) {
264 if (rootsDone) {
265 System.out.println("ERROR: Late root " + l);
266 print(lines);
267 return;
268 }
269 rootLines.add(l);
270 continue;
271 }
272
273 rootsDone = true;
274
275 if (currentHead == null) {
276 currentHead = referrer;
277 } else {
278 if (!currentHead.equals(referrer)) {
279 completedReferrers.add(currentHead);
280 currentHead = referrer;
281 if (completedReferrers.contains(referrer)) {
282 System.out.println("Non-contiguous referrer " + l);
283 print(lines);
284 return;
285 }
286 }
287 }
288 nonRootLines.add(l);
289 }
290 }
291
292 // Sort (root order is not specified) and print the roots.
293 // TODO: What about extra roots? JNI and the interpreter seem to introduce those (though it
294 // isn't clear why a debuggable-AoT test doesn't have the same, at least for locals).
295 // For now, swallow duplicates, and resolve once we have the metadata for the roots.
296 {
297 Collections.sort(rootLines);
298 String lastRoot = null;
299 for (String l : rootLines) {
300 if (lastRoot != null && lastRoot.equals(l)) {
301 continue;
302 }
303 lastRoot = l;
304 System.out.println(l);
305 }
306 }
307
308 // Iterate through the lines, keeping track of which referrers are visited, to ensure the
309 // order is acceptable.
310 HashSet<String> enabled = new HashSet<>();
311 if (additionalEnabledReferrer != null) {
312 enabled.add(additionalEnabledReferrer);
313 }
314 // Always add "0@0".
315 enabled.add("0@0");
316
317 for (String l : lines) {
318 String referrer = getReferrer(l);
319 String referree = getReferree(l);
320 if (isRoot(referrer)) {
321 // For a root src, just enable the referree.
322 enabled.add(referree);
323 } else {
324 // Check that the referrer is enabled (may be visited).
325 if (!enabled.contains(referrer)) {
326 System.out.println("Referrer " + referrer + " not enabled: " + l);
327 print(lines);
328 return;
329 }
330 enabled.add(referree);
331 }
332 }
333
334 // Now just sort the non-root lines and output them
335 Collections.sort(nonRootLines);
336 for (String l : nonRootLines) {
337 System.out.println(l);
338 }
339
340 System.out.println("---");
341 }
342
343 public static boolean isRoot(String ref) {
344 return ref.startsWith("root@");
345 }
346
347 private static String getReferrer(String line) {
348 int i = line.indexOf(" --");
349 if (i <= 0) {
350 throw new IllegalArgumentException(line);
351 }
352 int j = line.indexOf(' ');
353 if (i != j) {
354 throw new IllegalArgumentException(line);
355 }
356 return line.substring(0, i);
357 }
358
359 private static String getReferree(String line) {
360 int i = line.indexOf("--> ");
361 if (i <= 0) {
362 throw new IllegalArgumentException(line);
363 }
364 int j = line.indexOf(' ', i + 4);
365 if (j < 0) {
366 throw new IllegalArgumentException(line);
367 }
368 return line.substring(i + 4, j);
369 }
370
371 private static void print(String[] lines) {
372 for (String l : lines) {
373 System.out.println(l);
374 }
375 }
376 }
377
Andreas Gampe8da6d032016-10-31 19:31:03 -0700378 private static native void setupGcCallback();
379 private static native void enableGcTracking(boolean enable);
380 private static native int getGcStarts();
381 private static native int getGcFinishes();
382 private static native void forceGarbageCollection();
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700383
384 private static native void setTag(Object o, long tag);
385 private static native long getTag(Object o);
386
387 private static native String[] followReferences(int heapFilter, Class<?> klassFilter,
388 Object initialObject, int stopAfter, int followSet, Object jniRef);
Andreas Gampe8da6d032016-10-31 19:31:03 -0700389}