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
17import java.io.File;
18import java.io.IOException;
19import java.lang.reflect.Method;
20import java.util.concurrent.ConcurrentSkipListMap;
21import java.util.HashMap;
22import java.util.HashSet;
23import java.util.LinkedHashMap;
24import java.util.LinkedHashSet;
25import java.util.Map;
26import java.util.Set;
27import java.util.TreeMap;
28import java.util.TreeSet;
29
30public 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
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
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.
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
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.
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
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        }
244        public static void stopMethodTracing() throws Exception {
245            stopMethodTracingMethod.invoke(null);
246        }
247        public static int getMethodTracingMode() throws Exception {
248            return (int) getMethodTracingModeMethod.invoke(null);
249        }
250    }
251}
252