1 /*
2  * Copyright (C) 2015 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 import java.io.File;
18 import java.io.IOException;
19 import java.lang.reflect.Method;
20 import java.util.concurrent.ConcurrentSkipListMap;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedHashSet;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.TreeMap;
28 import java.util.TreeSet;
29 
30 public class Main {
31     private static final String TEMP_FILE_NAME_PREFIX = "test";
32     private static final String TEMP_FILE_NAME_SUFFIX = ".trace";
33     private static File file;
34 
main(String[] args)35     public static void main(String[] args) throws Exception {
36         String name = System.getProperty("java.vm.name");
37         if (!"Dalvik".equals(name)) {
38             System.out.println("This test is not supported on " + name);
39             return;
40         }
41         file = createTempFile();
42         try {
43             new Main().ensureCaller(true, 0);
44             new Main().ensureCaller(false, 0);
45         } finally {
46             if (file != null) {
47               file.delete();
48             }
49         }
50     }
51 
createTempFile()52     private static File createTempFile() throws Exception {
53         try {
54             return  File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
55         } catch (IOException e) {
56             System.setProperty("java.io.tmpdir", "/data/local/tmp");
57             try {
58                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
59             } catch (IOException e2) {
60                 System.setProperty("java.io.tmpdir", "/sdcard");
61                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
62             }
63         }
64     }
65 
66     // We make sure 'doLoadsOfStuff' has a caller, because it is this caller that will be
67     // pushed in the side instrumentation frame.
ensureCaller(boolean warmup, int invocationCount)68     public void ensureCaller(boolean warmup, int invocationCount) throws Exception {
69         doLoadsOfStuff(warmup, invocationCount);
70     }
71 
72     // The number of recursive calls we are going to do in 'doLoadsOfStuff' to ensure
73     // the JIT sees it hot.
74     static final int NUMBER_OF_INVOCATIONS = 5;
75 
doLoadsOfStuff(boolean warmup, int invocationCount)76     public void doLoadsOfStuff(boolean warmup, int invocationCount) throws Exception {
77         // Warmup is to make sure the JIT gets a chance to compile 'doLoadsOfStuff'.
78         if (warmup) {
79             if (invocationCount < NUMBER_OF_INVOCATIONS) {
80                 doLoadsOfStuff(warmup, ++invocationCount);
81             } else {
82                 // Give the JIT a chance to compiler.
83                 Thread.sleep(1000);
84             }
85         } else {
86             if (invocationCount == 0) {
87                 // When running the trace in trace mode, there is already a trace running.
88                 if (VMDebug.getMethodTracingMode() != 0) {
89                     VMDebug.stopMethodTracing();
90                 }
91                 VMDebug.startMethodTracing(file.getPath(), 0, 0, false, 0);
92             }
93             fillJit();
94             if (invocationCount < NUMBER_OF_INVOCATIONS) {
95                 doLoadsOfStuff(warmup, ++invocationCount);
96             } else {
97                 VMDebug.stopMethodTracing();
98             }
99         }
100     }
101 
102     // This method creates enough profiling data to fill the code cache and trigger
103     // a collection in debug mode (at the time of the test 10KB of data space). We
104     // used to crash by not looking at the instrumentation stack and deleting JIT code
105     // that will be later restored by the instrumentation.
fillJit()106     public static void fillJit() throws Exception {
107         Map map = new HashMap();
108         map.put("foo", "bar");
109         map.clear();
110         map.containsKey("foo");
111         map.containsValue("foo");
112         map.entrySet();
113         map.equals(map);
114         map.hashCode();
115         map.isEmpty();
116         map.keySet();
117         map.putAll(map);
118         map.remove("foo");
119         map.size();
120         map.put("bar", "foo");
121         map.values();
122 
123         map = new LinkedHashMap();
124         map.put("foo", "bar");
125         map.clear();
126         map.containsKey("foo");
127         map.containsValue("foo");
128         map.entrySet();
129         map.equals(map);
130         map.hashCode();
131         map.isEmpty();
132         map.keySet();
133         map.putAll(map);
134         map.remove("foo");
135         map.size();
136         map.put("bar", "foo");
137         map.values();
138 
139         map = new TreeMap();
140         map.put("foo", "bar");
141         map.clear();
142         map.containsKey("foo");
143         map.containsValue("foo");
144         map.entrySet();
145         map.equals(map);
146         map.hashCode();
147         map.isEmpty();
148         map.keySet();
149         map.putAll(map);
150         map.remove("foo");
151         map.size();
152         map.put("bar", "foo");
153         map.values();
154 
155         map = new ConcurrentSkipListMap();
156         map.put("foo", "bar");
157         map.clear();
158         map.containsKey("foo");
159         map.containsValue("foo");
160         map.entrySet();
161         map.equals(map);
162         map.hashCode();
163         map.isEmpty();
164         map.keySet();
165         map.putAll(map);
166         map.remove("foo");
167         map.size();
168         map.put("bar", "foo");
169         map.values();
170 
171         Set set = new HashSet();
172         set.add("foo");
173         set.addAll(set);
174         set.clear();
175         set.contains("foo");
176         set.containsAll(set);
177         set.equals(set);
178         set.hashCode();
179         set.isEmpty();
180         set.iterator();
181         set.remove("foo");
182         set.removeAll(set);
183         set.retainAll(set);
184         set.size();
185         set.add("foo");
186         set.toArray();
187 
188         set = new LinkedHashSet();
189         set.add("foo");
190         set.addAll(set);
191         set.clear();
192         set.contains("foo");
193         set.containsAll(set);
194         set.equals(set);
195         set.hashCode();
196         set.isEmpty();
197         set.iterator();
198         set.remove("foo");
199         set.removeAll(set);
200         set.retainAll(set);
201         set.size();
202         set.add("foo");
203         set.toArray();
204 
205         set = new TreeSet();
206         set.add("foo");
207         set.addAll(set);
208         set.clear();
209         set.contains("foo");
210         set.containsAll(set);
211         set.equals(set);
212         set.hashCode();
213         set.isEmpty();
214         set.iterator();
215         set.remove("foo");
216         set.removeAll(set);
217         set.retainAll(set);
218         set.size();
219         set.add("foo");
220         set.toArray();
221     }
222 
223     private static class VMDebug {
224         private static final Method startMethodTracingMethod;
225         private static final Method stopMethodTracingMethod;
226         private static final Method getMethodTracingModeMethod;
227         static {
228             try {
229                 Class<?> c = Class.forName("dalvik.system.VMDebug");
230                 startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
231                         Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE);
232                 stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
233                 getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
234             } catch (Exception e) {
235                 throw new RuntimeException(e);
236             }
237         }
238 
startMethodTracing(String filename, int bufferSize, int flags, boolean samplingEnabled, int intervalUs)239         public static void startMethodTracing(String filename, int bufferSize, int flags,
240                 boolean samplingEnabled, int intervalUs) throws Exception {
241             startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled,
242                     intervalUs);
243         }
stopMethodTracing()244         public static void stopMethodTracing() throws Exception {
245             stopMethodTracingMethod.invoke(null);
246         }
getMethodTracingMode()247         public static int getMethodTracingMode() throws Exception {
248             return (int) getMethodTracingModeMethod.invoke(null);
249         }
250     }
251 }
252