1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package android.jvmti.cts;
15 
16 import static org.junit.Assert.assertEquals;
17 import static org.junit.Assert.assertTrue;
18 
19 import java.lang.ref.WeakReference;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.LinkedList;
23 import java.util.List;
24 import org.junit.Before;
25 import org.junit.Test;
26 
27 import art.Main;
28 
29 /**
30  * Check tagging-related functionality.
31  */
32 public class JvmtiTaggingTest extends JvmtiTestBase {
33 
test()34     private static WeakReference<Object> test() {
35         Object o1 = new Object();
36         Main.setTag(o1, 1);
37 
38         Object o2 = new Object();
39         Main.setTag(o2, 2);
40 
41         assertEquals(1, Main.getTag(o1));
42         assertEquals(2, Main.getTag(o2));
43 
44         Runtime.getRuntime().gc();
45         Runtime.getRuntime().gc();
46 
47         assertEquals(1, Main.getTag(o1));
48         assertEquals(2, Main.getTag(o2));
49 
50         Runtime.getRuntime().gc();
51         Runtime.getRuntime().gc();
52 
53         Main.setTag(o1, 10);
54         Main.setTag(o2, 20);
55 
56         assertEquals(10, Main.getTag(o1));
57         assertEquals(20, Main.getTag(o2));
58 
59         return new WeakReference<Object>(o1);
60     }
61 
62     // Very simplistic tagging.
63     @Test
testTagging()64     public void testTagging() throws Exception {
65         test();
66     }
67 
68     @Test
testTaggingGC()69     public void testTaggingGC() {
70         WeakReference<Object> weak = test();
71 
72         Runtime.getRuntime().gc();
73         Runtime.getRuntime().gc();
74 
75         if (weak.get() != null) {
76             throw new RuntimeException("WeakReference not cleared");
77         }
78     }
79 
80     private ArrayList<Object> l;
81 
82     @Test
testGetTaggedObjects()83     public void testGetTaggedObjects() {
84         // Use an array list to ensure that the objects stay live for a bit. Also gives us a source
85         // to compare to. We use index % 10 as the tag.
86         l = new ArrayList<>();
87 
88         for (int i = 0; i < 20; i++) {
89             Integer o = new Integer(i);
90             l.add(o);
91             if (i % 10 != 0) {
92                 Main.setTag(o, i % 10);
93             }
94         }
95 
96         GetTaggedObjectsExpectation exp1 = new GetTaggedObjectsExpectation(18);
97         getTaggedObjectsRun(null, false, false, exp1);
98 
99         GetTaggedObjectsExpectation exp2 = new GetTaggedObjectsExpectation(18);
100         exp2.add(l.get(1), 1).add(l.get(11), 1).add(l.get(2), 2).add(l.get(12), 2).add(l.get(3), 3)
101                 .add(l.get(13), 3).add(l.get(4), 4).add(l.get(14), 4).add(l.get(5), 5)
102                 .add(l.get(15), 5).add(l.get(6), 6).add(l.get(16), 6).add(l.get(7), 7)
103                 .add(l.get(17), 7).add(l.get(8), 8).add(l.get(18), 8).add(l.get(9), 9)
104                 .add(l.get(19), 9);
105         getTaggedObjectsRun(null, true, true, exp2);
106 
107         GetTaggedObjectsExpectation exp3 = new GetTaggedObjectsExpectation(4);
108         exp3.add(l.get(2), 2).add(l.get(12), 2).add(l.get(5), 5).add(l.get(15), 5);
109         getTaggedObjectsRun(new long[] {2, 5}, true, true, exp3);
110 
111         GetTaggedObjectsExpectation exp4 = new GetTaggedObjectsExpectation(18);
112         exp4.add(null, 1).add(null, 1).add(null, 2).add(null, 2).add(null, 3).add(null, 3)
113                 .add(null, 4).add(null, 4).add(null, 5).add(null, 5).add(null, 6).add(null, 6)
114                 .add(null, 7).add(null, 7).add(null, 8).add(null, 8).add(null, 9).add(null, 9);
115         getTaggedObjectsRun(null, false, true, exp4);
116 
117         GetTaggedObjectsExpectation exp5 = new GetTaggedObjectsExpectation(18);
118         for (int i = 0; i < l.size(); i++) {
119             if (i % 10 != 0) {
120                 exp5.add(l.get(i), 0);
121             }
122         }
123         getTaggedObjectsRun(null, true, false, exp5);
124 
125         l = null;
126         Runtime.getRuntime().gc();
127         Runtime.getRuntime().gc();
128     }
129 
getTaggedObjectsRun(long[] searchTags, boolean returnObjects, boolean returnTags, GetTaggedObjectsExpectation exp)130     private static void getTaggedObjectsRun(long[] searchTags, boolean returnObjects,
131             boolean returnTags, GetTaggedObjectsExpectation exp) {
132         Object[] result = getTaggedObjects(searchTags, returnObjects, returnTags);
133 
134         Object[] objects = (Object[]) result[0];
135         long[] tags = (long[]) result[1];
136         int count = (int) result[2];
137 
138         exp.check(count, objects, tags);
139     }
140 
141     private static class GetTaggedObjectsExpectation {
142         List<Pair> expectations = new LinkedList<>();
143         int count;
144 
GetTaggedObjectsExpectation(int c)145         public GetTaggedObjectsExpectation(int c) {
146             count = c;
147         }
148 
check(int count, Object[] objects, long[] tags)149         public void check(int count, Object[] objects, long[] tags) {
150             assertEquals(this.count, count);
151 
152             if (objects == null && tags == null) {
153                 assertTrue(expectations.isEmpty());
154                 return;
155             }
156 
157             int l1 = objects == null ? 0 : objects.length;
158             int l2 = tags == null ? 0 : tags.length;
159             int l = Math.max(l1, l2);
160             List<Pair> tmp = new ArrayList<>(l);
161             for (int i = 0; i < l; i++) {
162                 tmp.add(new Pair(objects == null ? null : objects[i], tags == null ? 0 : tags[i]));
163             }
164             Collections.sort(tmp);
165             Collections.sort(expectations);
166 
167             if (!expectations.equals(tmp)) {
168                 for (int i = 0; i < expectations.size(); i++) {
169                     Pair p1 = expectations.get(i);
170                     Pair p2 = tmp.get(i);
171                     if (!p1.equals(p2)) {
172                         String s = "Not equal: " + p1 + "[" + System.identityHashCode(p1.obj) +
173                                 "] vs " + p2 + "[" + System.identityHashCode(p2.obj);
174                         throw new RuntimeException(s);
175                     }
176                 }
177             }
178 
179             assertEquals(expectations, tmp);
180         }
181 
add(Object o, long l)182         public GetTaggedObjectsExpectation add(Object o, long l) {
183             expectations.add(new Pair(o, l));
184             return this;
185         }
186     }
187 
188     private static class Pair implements Comparable<Pair> {
189         Object obj;
190         long tag;
191 
Pair(Object o, long t)192         public Pair(Object o, long t) {
193             obj = o;
194             tag = t;
195         }
196 
197         @Override
equals(Object obj)198         public boolean equals(Object obj) {
199             if (obj instanceof Pair) {
200                 Pair p = (Pair)obj;
201                 return tag == p.tag && (p.obj == null ? this.obj == null : p.obj.equals(this.obj));
202             }
203             return false;
204         }
205 
206         @Override
207         @SuppressWarnings("unchecked")
compareTo(Pair p)208         public int compareTo(Pair p) {
209             if (tag != p.tag) {
210                 return Long.compare(tag, p.tag);
211             }
212 
213             if ((obj instanceof Comparable) && (p.obj instanceof Comparable)) {
214                 // It's not really correct, but w/e, best effort.
215                 int result = ((Comparable<Object>) obj).compareTo(p.obj);
216                 if (result != 0) {
217                     return result;
218                 }
219             }
220 
221             if (obj != null && p.obj != null) {
222                 return obj.hashCode() - p.obj.hashCode();
223             }
224 
225             if (obj != null) {
226                 return 1;
227             }
228 
229             if (p.obj != null) {
230                 return -1;
231             }
232 
233             return hashCode() - p.hashCode();
234         }
235 
236         @Override
toString()237         public String toString() {
238             return "<" + obj + ";" + tag + ">";
239         }
240     }
241 
getTaggedObjects(long[] searchTags, boolean returnObjects, boolean returnTags)242     private static native Object[] getTaggedObjects(long[] searchTags, boolean returnObjects,
243             boolean returnTags);
244 }
245