blob: 5a11a5b14435af95c7033533984838be3957b733 [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 {
Andreas Gampe8da6d032016-10-31 19:31:03 -070024 doTest();
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070025 doFollowReferencesTest();
Andreas Gampe8da6d032016-10-31 19:31:03 -070026 }
27
28 public static void doTest() throws Exception {
29 setupGcCallback();
30
31 enableGcTracking(true);
32 run();
33 enableGcTracking(false);
34 }
35
36 private static void run() {
Andreas Gampeef3ace02016-11-01 13:58:14 -070037 clearStats();
Andreas Gampe8da6d032016-10-31 19:31:03 -070038 forceGarbageCollection();
39 printStats();
40 }
41
Andreas Gampeef3ace02016-11-01 13:58:14 -070042 private static void clearStats() {
43 getGcStarts();
44 getGcFinishes();
45 }
46
Andreas Gampe8da6d032016-10-31 19:31:03 -070047 private static void printStats() {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070048 System.out.println("---");
49 int s = getGcStarts();
50 int f = getGcFinishes();
51 System.out.println((s > 0) + " " + (f > 0));
52 }
53
54 public static void doFollowReferencesTest() throws Exception {
55 // Force GCs to clean up dirt.
56 Runtime.getRuntime().gc();
57 Runtime.getRuntime().gc();
58
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070059 setTag(Thread.currentThread(), 3000);
60
61 {
62 ArrayList<Object> tmpStorage = new ArrayList<>();
63 doFollowReferencesTestNonRoot(tmpStorage);
64 tmpStorage = null;
65 }
66
67 // Force GCs to clean up dirt.
68 Runtime.getRuntime().gc();
69 Runtime.getRuntime().gc();
70
71 doFollowReferencesTestRoot();
72
73 // Force GCs to clean up dirt.
74 Runtime.getRuntime().gc();
75 Runtime.getRuntime().gc();
76 }
77
78 private static void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) {
Andreas Gampe5d139fc2016-11-09 22:54:25 -080079 Verifier v = new Verifier();
80 tagClasses(v);
81 A a = createTree(v);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070082 tmpStorage.add(a);
Andreas Gampe5d139fc2016-11-09 22:54:25 -080083 v.add("0@0", "1@1000"); // tmpStorage[0] --(array-element)--> a.
84
85 doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null, v, null);
Andreas Gampe638a6932016-12-02 19:11:17 -080086 doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, null, v, "3@1001");
Andreas Gampe5d139fc2016-11-09 22:54:25 -080087
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070088 tmpStorage.clear();
89 }
90
91 private static void doFollowReferencesTestRoot() {
Andreas Gampe5d139fc2016-11-09 22:54:25 -080092 Verifier v = new Verifier();
93 tagClasses(v);
94 A a = createTree(v);
95
96 doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a, v, null);
Andreas Gampe638a6932016-12-02 19:11:17 -080097 doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, a, v, "3@1001");
Andreas Gampe70bfc8a2016-11-03 11:04:15 -070098 }
99
100 private static void doFollowReferencesTestImpl(A root, int stopAfter, int followSet,
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800101 Object asRoot, Verifier v, String additionalEnabled) {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700102 String[] lines =
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800103 followReferences(0, null, root, stopAfter, followSet, asRoot);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700104
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800105 v.process(lines, additionalEnabled);
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700106
107 // TODO: Test filters.
108 }
109
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800110 private static void tagClasses(Verifier v) {
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700111 setTag(A.class, 1000);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800112
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700113 setTag(B.class, 1001);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800114 v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class.
115
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700116 setTag(C.class, 1002);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800117 v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class.
118 v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class.
119
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700120 setTag(I1.class, 2000);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800121
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700122 setTag(I2.class, 2001);
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800123 v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700124 }
125
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800126 private static A createTree(Verifier v) {
127 A aInst = new A();
128 setTag(aInst, 1);
129 String aInstStr = "1@1000";
130 String aClassStr = "1000@0";
131 v.add(aInstStr, aClassStr); // A -->(class) --> A.class.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700132
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800133 A a2Inst = new A();
134 setTag(a2Inst, 2);
135 aInst.foo = a2Inst;
136 String a2InstStr = "2@1000";
137 v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class.
138 v.add(aInstStr, a2InstStr); // A -->(field) --> A2.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700139
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800140 B bInst = new B();
141 setTag(bInst, 3);
142 aInst.foo2 = bInst;
143 String bInstStr = "3@1001";
144 String bClassStr = "1001@0";
145 v.add(bInstStr, bClassStr); // B -->(class) --> B.class.
146 v.add(aInstStr, bInstStr); // A -->(field) --> B.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700147
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800148 A a3Inst = new A();
149 setTag(a3Inst, 4);
150 bInst.bar = a3Inst;
151 String a3InstStr = "4@1000";
152 v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class.
153 v.add(bInstStr, a3InstStr); // B -->(field) --> A3.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700154
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800155 C cInst = new C();
156 setTag(cInst, 5);
157 bInst.bar2 = cInst;
158 String cInstStr = "5@1000";
159 String cClassStr = "1002@0";
160 v.add(cInstStr, cClassStr); // C -->(class) --> C.class.
161 v.add(bInstStr, cInstStr); // B -->(field) --> C.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700162
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800163 A a4Inst = new A();
164 setTag(a4Inst, 6);
165 cInst.baz = a4Inst;
166 String a4InstStr = "6@1000";
167 v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class.
168 v.add(cInstStr, a4InstStr); // C -->(field) --> A4.
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700169
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800170 cInst.baz2 = aInst;
171 v.add(cInstStr, aInstStr); // C -->(field) --> A.
172
173 return aInst;
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700174 }
175
176 public static class A {
177 public A foo;
178 public A foo2;
179
180 public A() {}
181 public A(A a, A b) {
182 foo = a;
183 foo2 = b;
184 }
185 }
186
187 public static class B extends A {
188 public A bar;
189 public A bar2;
190
191 public B() {}
192 public B(A a, A b) {
193 bar = a;
194 bar2 = b;
195 }
196 }
197
198 public static interface I1 {
199 public final static int i1Field = 1;
200 }
201
202 public static interface I2 extends I1 {
203 public final static int i2Field = 2;
204 }
205
206 public static class C extends B implements I2 {
207 public A baz;
208 public A baz2;
209
210 public C() {}
211 public C(A a, A b) {
212 baz = a;
213 baz2 = b;
214 }
Andreas Gampe8da6d032016-10-31 19:31:03 -0700215 }
216
Andreas Gampe5d139fc2016-11-09 22:54:25 -0800217 public static class Verifier {
218 public static class Node {
219 public String referrer;
220
221 public HashSet<String> referrees = new HashSet<>();
222
223 public Node(String r) {
224 referrer = r;
225 }
226
227 public boolean isRoot() {
228 return referrer.startsWith("root@");
229 }
230 }
231
232 HashMap<String, Node> nodes = new HashMap<>();
233
234 public Verifier() {
235 }
236
237 public void add(String referrer, String referree) {
238 if (!nodes.containsKey(referrer)) {
239 nodes.put(referrer, new Node(referrer));
240 }
241 if (referree != null) {
242 nodes.get(referrer).referrees.add(referree);
243 }
244 }
245
246 public void process(String[] lines, String additionalEnabledReferrer) {
247 // This method isn't optimal. The loops could be merged. However, it's more readable if
248 // the different parts are separated.
249
250 ArrayList<String> rootLines = new ArrayList<>();
251 ArrayList<String> nonRootLines = new ArrayList<>();
252
253 // Check for consecutive chunks of referrers. Also ensure roots come first.
254 {
255 String currentHead = null;
256 boolean rootsDone = false;
257 HashSet<String> completedReferrers = new HashSet<>();
258 for (String l : lines) {
259 String referrer = getReferrer(l);
260
261 if (isRoot(referrer)) {
262 if (rootsDone) {
263 System.out.println("ERROR: Late root " + l);
264 print(lines);
265 return;
266 }
267 rootLines.add(l);
268 continue;
269 }
270
271 rootsDone = true;
272
273 if (currentHead == null) {
274 currentHead = referrer;
275 } else {
276 if (!currentHead.equals(referrer)) {
277 completedReferrers.add(currentHead);
278 currentHead = referrer;
279 if (completedReferrers.contains(referrer)) {
280 System.out.println("Non-contiguous referrer " + l);
281 print(lines);
282 return;
283 }
284 }
285 }
286 nonRootLines.add(l);
287 }
288 }
289
290 // Sort (root order is not specified) and print the roots.
291 // TODO: What about extra roots? JNI and the interpreter seem to introduce those (though it
292 // isn't clear why a debuggable-AoT test doesn't have the same, at least for locals).
293 // For now, swallow duplicates, and resolve once we have the metadata for the roots.
294 {
295 Collections.sort(rootLines);
296 String lastRoot = null;
297 for (String l : rootLines) {
298 if (lastRoot != null && lastRoot.equals(l)) {
299 continue;
300 }
301 lastRoot = l;
302 System.out.println(l);
303 }
304 }
305
306 // Iterate through the lines, keeping track of which referrers are visited, to ensure the
307 // order is acceptable.
308 HashSet<String> enabled = new HashSet<>();
309 if (additionalEnabledReferrer != null) {
310 enabled.add(additionalEnabledReferrer);
311 }
312 // Always add "0@0".
313 enabled.add("0@0");
314
315 for (String l : lines) {
316 String referrer = getReferrer(l);
317 String referree = getReferree(l);
318 if (isRoot(referrer)) {
319 // For a root src, just enable the referree.
320 enabled.add(referree);
321 } else {
322 // Check that the referrer is enabled (may be visited).
323 if (!enabled.contains(referrer)) {
324 System.out.println("Referrer " + referrer + " not enabled: " + l);
325 print(lines);
326 return;
327 }
328 enabled.add(referree);
329 }
330 }
331
332 // Now just sort the non-root lines and output them
333 Collections.sort(nonRootLines);
334 for (String l : nonRootLines) {
335 System.out.println(l);
336 }
337
338 System.out.println("---");
339 }
340
341 public static boolean isRoot(String ref) {
342 return ref.startsWith("root@");
343 }
344
345 private static String getReferrer(String line) {
346 int i = line.indexOf(" --");
347 if (i <= 0) {
348 throw new IllegalArgumentException(line);
349 }
350 int j = line.indexOf(' ');
351 if (i != j) {
352 throw new IllegalArgumentException(line);
353 }
354 return line.substring(0, i);
355 }
356
357 private static String getReferree(String line) {
358 int i = line.indexOf("--> ");
359 if (i <= 0) {
360 throw new IllegalArgumentException(line);
361 }
362 int j = line.indexOf(' ', i + 4);
363 if (j < 0) {
364 throw new IllegalArgumentException(line);
365 }
366 return line.substring(i + 4, j);
367 }
368
369 private static void print(String[] lines) {
370 for (String l : lines) {
371 System.out.println(l);
372 }
373 }
374 }
375
Andreas Gampe8da6d032016-10-31 19:31:03 -0700376 private static native void setupGcCallback();
377 private static native void enableGcTracking(boolean enable);
378 private static native int getGcStarts();
379 private static native int getGcFinishes();
380 private static native void forceGarbageCollection();
Andreas Gampe70bfc8a2016-11-03 11:04:15 -0700381
382 private static native void setTag(Object o, long tag);
383 private static native long getTag(Object o);
384
385 private static native String[] followReferences(int heapFilter, Class<?> klassFilter,
386 Object initialObject, int stopAfter, int followSet, Object jniRef);
Andreas Gampe8da6d032016-10-31 19:31:03 -0700387}