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, 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 const char* argv[argc+1]; 73 const char* kVerifyArg; 74 const char* kDexOptArg; 75 int timeoutMult; 76 pid_t pid; 77 struct timeval startWhen, endWhen; 78 char propBuf[PROPERTY_VALUE_MAX]; 79 char execModeBuf[PROPERTY_VALUE_MAX + sizeof("-X")]; 80 bool verifyJava = true; 81 82 property_get("dalvik.vm.verify-bytecode", propBuf, ""); 83 if (strcmp(propBuf, "true") == 0) { 84 verifyJava = true; 85 } else if (strcmp(propBuf, "false") == 0) { 86 verifyJava = false; 87 } else { 88 /* bad value or not defined; use default */ 89 } 90 91 if (verifyJava) { 92 kVerifyArg = "-Xverify:all"; 93 kDexOptArg = "-Xdexopt:verified"; 94 timeoutMult = 11; 95 } else { 96 kVerifyArg = "-Xverify:none"; 97 //kDexOptArg = "-Xdexopt:all"; 98 kDexOptArg = "-Xdexopt:verified"; 99 timeoutMult = 7; 100 } 101 102 property_get("dalvik.vm.execution-mode", propBuf, ""); 103 if (strncmp(propBuf, "int:", 4) == 0) { 104 strcpy(execModeBuf, "-X"); 105 strcat(execModeBuf, propBuf); 106 kExecMode = execModeBuf; 107 } 108 109 LOGV("TouchDex trampoline forking\n"); 110 gettimeofday(&startWhen, NULL); 111 112 /* 113 * Retrieve strings. Note we want to do this *before* the fork() -- bad 114 * idea to perform Java operations in the child process (not all threads 115 * get carried over to the new process). 116 */ 117 bcp = env->GetStringUTFChars(bcpStr, NULL); 118 dexFiles = env->GetStringUTFChars(dexFilesStr, NULL); 119 if (bcp == NULL || dexFiles == NULL) { 120 LOGE("Bad values for bcp=%p dexFiles=%p\n", bcp, dexFiles); 121 abort(); 122 } 123 124 pid = fork(); 125 if (pid < 0) { 126 LOGE("fork failed: %s", strerror(errno)); 127 return -1; 128 } 129 130 if (pid == 0) { 131 /* child */ 132 char* bcpArg; 133 134 LOGV("TouchDex trampoline in child\n"); 135 136 bcpArg = (char*) malloc(strlen(bcp) + strlen(kBcpArgName) +1); 137 strcpy(bcpArg, kBcpArgName); 138 strcat(bcpArg, bcp); 139 140 argv[0] = kExecFile; 141 argv[1] = bcpArg; 142 argv[2] = kVerifyArg; 143 argv[3] = kDexOptArg; 144 argv[4] = kExecMode; 145 argv[5] = kClassName; 146 argv[6] = dexFiles; 147 argv[7] = NULL; 148 149 //LOGI("Calling execv with args:\n"); 150 //for (int i = 0; i < argc; i++) 151 // LOGI(" %d: '%s'\n", i, argv[i]); 152 153 execv(kExecFile, (char* const*) argv); 154 free(bcpArg); 155 156 LOGE("execv '%s' failed: %s\n", kExecFile, strerror(errno)); 157 exit(1); 158 } else { 159 int cc, count, dexCount, timeout; 160 int result = -1; 161 const char* cp; 162 163 /* 164 * Adjust the timeout based on how many DEX files we have to 165 * process. Larger DEX files take longer, so this is a crude 166 * approximation at best. 167 * 168 * We need this for http://b/issue?id=836771, which can leave us 169 * stuck waiting for a long time even if there is no work to be done. 170 * 171 * This is currently being (ab)used to convert single files, which 172 * sort of spoils the timeout calculation. We establish a minimum 173 * timeout for single apps. 174 * 175 * The timeout calculation doesn't work at all right when a 176 * directory is specified. So the minimum is now a minute. At 177 * this point it's probably safe to just remove the timeout. 178 * 179 * The timeout is in 1/10ths of a second. 180 */ 181 dexCount = 1; 182 cp = dexFiles; 183 while (*++cp != '\0') { 184 if (*cp == ':') 185 dexCount++; 186 } 187 timeout = timeoutMult * dexCount; 188 if (timeout < kMinTimeout) 189 timeout = kMinTimeout; 190 191 env->ReleaseStringUTFChars(bcpStr, bcp); 192 env->ReleaseStringUTFChars(dexFilesStr, dexFiles); 193 194 195 LOGD("TouchDex parent waiting for pid=%d (timeout=%.1fs)\n", 196 (int) pid, timeout / 10.0); 197 for (count = 0; count < timeout; count++) { 198 /* waitpid doesn't take a timeout, so poll and sleep */ 199 cc = waitpid(pid, &result, WNOHANG); 200 if (cc < 0) { 201 LOGE("waitpid(%d) failed: %s", (int) pid, strerror(errno)); 202 return -1; 203 } else if (cc == 0) { 204 usleep(100000); /* 0.1 sec */ 205 } else { 206 /* success! */ 207 break; 208 } 209 } 210 211 if (count == timeout) { 212 /* note kill(0) returns 0 if the pid is a zombie */ 213 LOGE("timed out waiting for %d; kill(0) returns %d\n", 214 (int) pid, kill(pid, 0)); 215 logProcStatus(pid); 216 } else { 217 LOGV("TouchDex done after %d iterations (kill(0) returns %d)\n", 218 count, kill(pid, 0)); 219 } 220 221 gettimeofday(&endWhen, NULL); 222 long long start = startWhen.tv_sec * 1000000 + startWhen.tv_usec; 223 long long end = endWhen.tv_sec * 1000000 + endWhen.tv_usec; 224 225 LOGI("Dalvik-cache prep: status=0x%04x, finished in %dms\n", 226 result, (int) ((end - start) / 1000)); 227 228 if (WIFEXITED(result)) 229 return WEXITSTATUS(result); 230 else 231 return result; 232 } 233 } 234 235 /* 236 * Dump the contents of /proc/<pid>/status to the log file. 237 */ 238 static void logProcStatus(pid_t pid) 239 { 240 char localBuf[256]; 241 FILE* fp; 242 243 sprintf(localBuf, "/proc/%d/status", (int) pid); 244 fp = fopen(localBuf, "r"); 245 if (fp == NULL) { 246 LOGI("Unable to open '%s'\n", localBuf); 247 return; 248 } 249 250 LOGI("Contents of %s:\n", localBuf); 251 while (true) { 252 fgets(localBuf, sizeof(localBuf), fp); 253 if (ferror(fp) || feof(fp)) 254 break; 255 LOGI(" %s", localBuf); 256 } 257 258 fclose(fp); 259 } 260 261 static JNINativeMethod gMethods[] = { 262 { "trampoline", "(Ljava/lang/String;Ljava/lang/String;)I", 263 (void*) dalvik_system_TouchDex_trampoline }, 264 }; 265 int register_dalvik_system_TouchDex(JNIEnv* env) { 266 return jniRegisterNativeMethods(env, JAVA_PACKAGE "/TouchDex", gMethods, NELEM(gMethods)); 267 } 268 269 }; // namespace android 270