1 /* 2 * Copyright (C) 2007 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 /* 18 * Bit of code to wrap DEX force-updating with a fork() call. 19 */ 20 21 #define LOG_TAG "TouchDex" 22 #include "JNIHelp.h" 23 24 #include "cutils/properties.h" 25 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <signal.h> 31 #include <sys/types.h> 32 #include <sys/wait.h> 33 #include <sys/time.h> 34 #include <assert.h> 35 #include <errno.h> 36 37 #define JAVA_PACKAGE "dalvik/system" 38 39 #ifndef HAVE_ANDROID_OS 40 # define BASE_DIR "/work/device/out/linux-x86-debug-sim" 41 #else 42 # define BASE_DIR "" 43 #endif 44 45 namespace android { 46 47 // fwd 48 static void logProcStatus(pid_t pid); 49 50 51 /* 52 * private static int trampoline(String dexFiles, String bcp) 53 */ 54 static jint dalvik_system_TouchDex_trampoline(JNIEnv* env, 55 jclass clazz, jstring dexFilesStr, jstring bcpStr) 56 { 57 #ifndef HAVE_ANDROID_OS 58 /* don't do this on simulator -- gdb goes "funny" in goobuntu */ 59 return 0; 60 #endif 61 62 const int kMinTimeout = 900; // 90 seconds 63 const char* bcp; 64 const char* dexFiles; 65 static const char* kExecFile = BASE_DIR "/system/bin/dalvikvm"; 66 //static const char* kDebugArg = 67 // "-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"; 68 static const char* kBcpArgName = "-Xbootclasspath:"; 69 static const char* kClassName = "dalvik.system.TouchDex"; 70 static const char* kExecMode = "-Xint"; 71 static const int argc = 7; 72 char* bcpArg; 73 const char* argv[argc+1]; 74 const char* kVerifyArg; 75 const char* kDexOptArg; 76 int timeoutMult; 77 pid_t pid; 78 struct timeval startWhen, endWhen; 79 char propBuf[PROPERTY_VALUE_MAX]; 80 char execModeBuf[PROPERTY_VALUE_MAX + sizeof("-X")]; 81 bool verifyJava = true; 82 83 property_get("dalvik.vm.verify-bytecode", propBuf, ""); 84 if (strcmp(propBuf, "true") == 0) { 85 verifyJava = true; 86 } else if (strcmp(propBuf, "false") == 0) { 87 verifyJava = false; 88 } else { 89 /* bad value or not defined; use default */ 90 } 91 92 if (verifyJava) { 93 kVerifyArg = "-Xverify:all"; 94 kDexOptArg = "-Xdexopt:verified"; 95 timeoutMult = 11; 96 } else { 97 kVerifyArg = "-Xverify:none"; 98 //kDexOptArg = "-Xdexopt:all"; 99 kDexOptArg = "-Xdexopt:verified"; 100 timeoutMult = 7; 101 } 102 103 property_get("dalvik.vm.execution-mode", propBuf, ""); 104 if (strncmp(propBuf, "int:", 4) == 0) { 105 strcpy(execModeBuf, "-X"); 106 strcat(execModeBuf, propBuf); 107 kExecMode = execModeBuf; 108 } 109 110 LOGV("TouchDex trampoline forking\n"); 111 gettimeofday(&startWhen, NULL); 112 113 /* 114 * Retrieve strings. Note we want to do this *before* the fork() -- bad 115 * idea to perform Java operations in the child process (not all threads 116 * get carried over to the new process). 117 */ 118 bcp = env->GetStringUTFChars(bcpStr, NULL); 119 dexFiles = env->GetStringUTFChars(dexFilesStr, NULL); 120 if (bcp == NULL || dexFiles == NULL) { 121 LOGE("Bad values for bcp=%p dexFiles=%p\n", bcp, dexFiles); 122 abort(); 123 } 124 125 pid = fork(); 126 if (pid < 0) { 127 LOGE("fork failed: %s", strerror(errno)); 128 return -1; 129 } 130 131 if (pid == 0) { 132 /* child */ 133 char* bcpArg; 134 135 LOGV("TouchDex trampoline in child\n"); 136 137 bcpArg = (char*) malloc(strlen(bcp) + strlen(kBcpArgName) +1); 138 strcpy(bcpArg, kBcpArgName); 139 strcat(bcpArg, bcp); 140 141 argv[0] = kExecFile; 142 argv[1] = bcpArg; 143 argv[2] = kVerifyArg; 144 argv[3] = kDexOptArg; 145 argv[4] = kExecMode; 146 argv[5] = kClassName; 147 argv[6] = dexFiles; 148 argv[7] = NULL; 149 150 //LOGI("Calling execv with args:\n"); 151 //for (int i = 0; i < argc; i++) 152 // LOGI(" %d: '%s'\n", i, argv[i]); 153 154 execv(kExecFile, (char* const*) argv); 155 free(bcpArg); 156 157 LOGE("execv '%s' failed: %s\n", kExecFile, strerror(errno)); 158 exit(1); 159 } else { 160 int cc, count, dexCount, timeout; 161 int result = -1; 162 const char* cp; 163 164 /* 165 * Adjust the timeout based on how many DEX files we have to 166 * process. Larger DEX files take longer, so this is a crude 167 * approximation at best. 168 * 169 * We need this for http://b/issue?id=836771, which can leave us 170 * stuck waiting for a long time even if there is no work to be done. 171 * 172 * This is currently being (ab)used to convert single files, which 173 * sort of spoils the timeout calculation. We establish a minimum 174 * timeout for single apps. 175 * 176 * The timeout calculation doesn't work at all right when a 177 * directory is specified. So the minimum is now a minute. At 178 * this point it's probably safe to just remove the timeout. 179 * 180 * The timeout is in 1/10ths of a second. 181 */ 182 dexCount = 1; 183 cp = dexFiles; 184 while (*++cp != '\0') { 185 if (*cp == ':') 186 dexCount++; 187 } 188 timeout = timeoutMult * dexCount; 189 if (timeout < kMinTimeout) 190 timeout = kMinTimeout; 191 192 env->ReleaseStringUTFChars(bcpStr, bcp); 193 env->ReleaseStringUTFChars(dexFilesStr, dexFiles); 194 195 196 LOGD("TouchDex parent waiting for pid=%d (timeout=%.1fs)\n", 197 (int) pid, timeout / 10.0); 198 for (count = 0; count < timeout; count++) { 199 /* waitpid doesn't take a timeout, so poll and sleep */ 200 cc = waitpid(pid, &result, WNOHANG); 201 if (cc < 0) { 202 LOGE("waitpid(%d) failed: %s", (int) pid, strerror(errno)); 203 return -1; 204 } else if (cc == 0) { 205 usleep(100000); /* 0.1 sec */ 206 } else { 207 /* success! */ 208 break; 209 } 210 } 211 212 if (count == timeout) { 213 /* note kill(0) returns 0 if the pid is a zombie */ 214 LOGE("timed out waiting for %d; kill(0) returns %d\n", 215 (int) pid, kill(pid, 0)); 216 logProcStatus(pid); 217 } else { 218 LOGV("TouchDex done after %d iterations (kill(0) returns %d)\n", 219 count, kill(pid, 0)); 220 } 221 222 gettimeofday(&endWhen, NULL); 223 long long start = startWhen.tv_sec * 1000000 + startWhen.tv_usec; 224 long long end = endWhen.tv_sec * 1000000 + endWhen.tv_usec; 225 226 LOGI("Dalvik-cache prep: status=0x%04x, finished in %dms\n", 227 result, (int) ((end - start) / 1000)); 228 229 if (WIFEXITED(result)) 230 return WEXITSTATUS(result); 231 else 232 return result; 233 } 234 } 235 236 /* 237 * Dump the contents of /proc/<pid>/status to the log file. 238 */ 239 static void logProcStatus(pid_t pid) 240 { 241 char localBuf[256]; 242 FILE* fp; 243 244 sprintf(localBuf, "/proc/%d/status", (int) pid); 245 fp = fopen(localBuf, "r"); 246 if (fp == NULL) { 247 LOGI("Unable to open '%s'\n", localBuf); 248 return; 249 } 250 251 LOGI("Contents of %s:\n", localBuf); 252 while (true) { 253 fgets(localBuf, sizeof(localBuf), fp); 254 if (ferror(fp) || feof(fp)) 255 break; 256 LOGI(" %s", localBuf); 257 } 258 259 fclose(fp); 260 } 261 262 /* 263 * JNI registration. 264 */ 265 static JNINativeMethod gMethods[] = { 266 /* name, signature, funcPtr */ 267 { "trampoline", "(Ljava/lang/String;Ljava/lang/String;)I", 268 (void*) dalvik_system_TouchDex_trampoline }, 269 }; 270 271 extern "C" int register_dalvik_system_TouchDex(JNIEnv* env) 272 { 273 return jniRegisterNativeMethods(env, JAVA_PACKAGE "/TouchDex", 274 gMethods, NELEM(gMethods)); 275 } 276 277 }; // namespace android 278 279