1 /*
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 
17 import java.io.File;
18 import java.io.IOException;
19 import java.lang.reflect.Method;
20 
21 public class Main {
22 
main(String[] args)23   public static void main(String[] args) throws Exception {
24     System.loadLibrary(args[0]);
25 
26     if (!hasJit()) {
27       // Test requires JIT for creating profiling infos.
28       return;
29     }
30 
31     File file = null;
32     File file2 = null;
33     File file3 = null;
34     try {
35       // Register `file2` with an empty jar. Even though `file2` is registered before `file`, the
36       // runtime should not write bootclasspath methods to `file2`, and it should not even create
37       // `file2`.
38       file2 = createTempFile();
39       String emptyJarPath =
40           System.getenv("DEX_LOCATION") + "/res/art-gtest-jars-MainEmptyUncompressed.jar";
41       VMRuntime.registerAppInfo("test.app",
42                                 file2.getPath(),
43                                 file2.getPath(),
44                                 new String[] {emptyJarPath},
45                                 VMRuntime.CODE_PATH_TYPE_SPLIT_APK);
46 
47       file = createTempFile();
48       String codePath = System.getenv("DEX_LOCATION") + "/595-profile-saving.jar";
49       VMRuntime.registerAppInfo("test.app",
50                                 file.getPath(),
51                                 file.getPath(),
52                                 new String[] {codePath},
53                                 VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
54 
55       file3 = createTempFile();
56       String dexPath = System.getenv("DEX_LOCATION") + "/res/art-gtest-jars-Main.dex";
57       VMRuntime.registerAppInfo("test.app",
58                                 file3.getPath(),
59                                 file3.getPath(),
60                                 new String[] {dexPath},
61                                 VMRuntime.CODE_PATH_TYPE_SPLIT_APK);
62 
63       // Delete the files so that we can check if the runtime creates them. The runtime should
64       // create `file` and `file3` but not `file2`.
65       file.delete();
66       file2.delete();
67       file3.delete();
68 
69       // Test that the runtime saves the profiling info of an app method in a .jar file.
70       Method appMethod = Main.class.getDeclaredMethod("testAddMethodToProfile",
71           File.class, Method.class);
72       testAddMethodToProfile(file, appMethod);
73 
74       // Test that the runtime saves the profiling info of an app method in a .dex file.
75       ClassLoader dexClassLoader = (ClassLoader) Class.forName("dalvik.system.PathClassLoader")
76                                            .getDeclaredConstructor(String.class, ClassLoader.class)
77                                            .newInstance(dexPath, null /* parent */);
78       Class<?> c = Class.forName("Main", true /* initialize */, dexClassLoader);
79       Method methodInDex = c.getMethod("main", (new String[0]).getClass());
80       testAddMethodToProfile(file3, methodInDex);
81 
82       // Test that the runtime saves the profiling info of a bootclasspath method.
83       Method bootMethod = File.class.getDeclaredMethod("exists");
84       if (bootMethod.getDeclaringClass().getClassLoader() != Object.class.getClassLoader()) {
85         System.out.println("Class loader does not match boot class");
86       }
87       testAddMethodToProfile(file, bootMethod);
88 
89       // We never expect System.console to be executed before Main.main gets invoked, and therefore
90       // it should never be in a profile.
91       Method bootNotInProfileMethod = System.class.getDeclaredMethod("console");
92       testMethodNotInProfile(file, bootNotInProfileMethod);
93 
94       testProfileNotExist(file2);
95 
96       if (!isForBootImage(file.getPath())) {
97         throw new Error("Expected profile to be for boot image");
98       }
99     } finally {
100       if (file != null) {
101         file.delete();
102       }
103       if (file2 != null) {
104         file2.delete();
105       }
106     }
107   }
108 
testAddMethodToProfile(File file, Method m)109   static void testAddMethodToProfile(File file, Method m) {
110     // Make sure we have a profile info for this method without the need to loop.
111     ensureProfilingInfo(m);
112     // Make sure the profile gets saved.
113     ensureProfileProcessing();
114     // Verify that the profile was saved and contains the method.
115     if (!presentInProfile(file.getPath(), m)) {
116       throw new RuntimeException("Expected method " + m + " to be in the profile");
117     }
118   }
119 
testMethodNotInProfile(File file, Method m)120   static void testMethodNotInProfile(File file, Method m) {
121     // Make sure the profile gets saved.
122     ensureProfileProcessing();
123     // Verify that the profile was saved and contains the method.
124     if (presentInProfile(file.getPath(), m)) {
125       throw new RuntimeException("Did not expect method " + m + " to be in the profile");
126     }
127   }
128 
testProfileNotExist(File file)129   static void testProfileNotExist(File file) {
130     // Make sure the profile saving has been attempted.
131     ensureProfileProcessing();
132     // Verify that the profile does not exist.
133     if (file.exists()) {
134       throw new RuntimeException("Did not expect " + file + " to exist");
135     }
136   }
137 
138   // Ensure a method has a profiling info.
ensureProfilingInfo(Method method)139   public static void ensureProfilingInfo(Method method) {
140     ensureJitBaselineCompiled(method.getDeclaringClass(), method.getName());
141   }
ensureJitBaselineCompiled(Class<?> cls, String methodName)142   public static native void ensureJitBaselineCompiled(Class<?> cls, String methodName);
143   // Ensures the profile saver does its usual processing.
ensureProfileProcessing()144   public static native void ensureProfileProcessing();
145   // Checks if the profiles saver knows about the method.
presentInProfile(String profile, Method method)146   public static native boolean presentInProfile(String profile, Method method);
147   // Returns true if the profile is for the boot image.
isForBootImage(String profile)148   public static native boolean isForBootImage(String profile);
hasJit()149   public static native boolean hasJit();
150 
151   private static final String TEMP_FILE_NAME_PREFIX = "temp";
152   private static final String TEMP_FILE_NAME_SUFFIX = "-file";
153 
getProfileInfoDump( String filename)154   static native String getProfileInfoDump(
155       String filename);
156 
createTempFile()157   private static File createTempFile() throws Exception {
158     try {
159       return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
160     } catch (IOException e) {
161       System.setProperty("java.io.tmpdir", "/data/local/tmp");
162       try {
163         return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
164       } catch (IOException e2) {
165         System.setProperty("java.io.tmpdir", "/sdcard");
166         return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
167       }
168     }
169   }
170 
171   private static class VMRuntime {
172     public static final int CODE_PATH_TYPE_PRIMARY_APK = 1 << 0;
173     public static final int CODE_PATH_TYPE_SPLIT_APK = 1 << 1;
174     private static final Method registerAppInfoMethod;
175 
176     static {
177       try {
178         Class<? extends Object> c = Class.forName("dalvik.system.VMRuntime");
179         registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
180             String.class, String.class, String.class, String[].class, int.class);
181       } catch (Exception e) {
182         throw new RuntimeException(e);
183       }
184     }
185 
registerAppInfo( String packageName, String curProfile, String refProfile, String[] codePaths, int codePathsType)186     public static void registerAppInfo(
187         String packageName,
188         String curProfile,
189         String refProfile,
190         String[] codePaths,
191         int codePathsType) throws Exception {
192       registerAppInfoMethod.invoke(
193           null,
194           packageName,
195           curProfile,
196           refProfile,
197           codePaths,
198           codePathsType);
199     }
200   }
201 }
202