| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Bit of code to wrap DEX force-updating with a fork() call. |
| */ |
| |
| #define LOG_TAG "TouchDex" |
| #include "JNIHelp.h" |
| |
| #include "cutils/properties.h" |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <sys/time.h> |
| #include <assert.h> |
| #include <errno.h> |
| |
| #define JAVA_PACKAGE "dalvik/system" |
| |
| #ifndef HAVE_ANDROID_OS |
| # define BASE_DIR "/work/device/out/linux-x86-debug-sim" |
| #else |
| # define BASE_DIR "" |
| #endif |
| |
| namespace android { |
| |
| // fwd |
| static void logProcStatus(pid_t pid); |
| |
| |
| /* |
| * private static int trampoline(String dexFiles, String bcp) |
| */ |
| static jint dalvik_system_TouchDex_trampoline(JNIEnv* env, |
| jclass, jstring dexFilesStr, jstring bcpStr) |
| { |
| #ifndef HAVE_ANDROID_OS |
| /* don't do this on simulator -- gdb goes "funny" in goobuntu */ |
| return 0; |
| #endif |
| |
| const int kMinTimeout = 900; // 90 seconds |
| const char* bcp; |
| const char* dexFiles; |
| static const char* kExecFile = BASE_DIR "/system/bin/dalvikvm"; |
| //static const char* kDebugArg = |
| // "-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"; |
| static const char* kBcpArgName = "-Xbootclasspath:"; |
| static const char* kClassName = "dalvik.system.TouchDex"; |
| static const char* kExecMode = "-Xint"; |
| static const int argc = 7; |
| const char* argv[argc+1]; |
| const char* kVerifyArg; |
| const char* kDexOptArg; |
| int timeoutMult; |
| pid_t pid; |
| struct timeval startWhen, endWhen; |
| char propBuf[PROPERTY_VALUE_MAX]; |
| char execModeBuf[PROPERTY_VALUE_MAX + sizeof("-X")]; |
| bool verifyJava = true; |
| |
| property_get("dalvik.vm.verify-bytecode", propBuf, ""); |
| if (strcmp(propBuf, "true") == 0) { |
| verifyJava = true; |
| } else if (strcmp(propBuf, "false") == 0) { |
| verifyJava = false; |
| } else { |
| /* bad value or not defined; use default */ |
| } |
| |
| if (verifyJava) { |
| kVerifyArg = "-Xverify:all"; |
| kDexOptArg = "-Xdexopt:verified"; |
| timeoutMult = 11; |
| } else { |
| kVerifyArg = "-Xverify:none"; |
| //kDexOptArg = "-Xdexopt:all"; |
| kDexOptArg = "-Xdexopt:verified"; |
| timeoutMult = 7; |
| } |
| |
| property_get("dalvik.vm.execution-mode", propBuf, ""); |
| if (strncmp(propBuf, "int:", 4) == 0) { |
| strcpy(execModeBuf, "-X"); |
| strcat(execModeBuf, propBuf); |
| kExecMode = execModeBuf; |
| } |
| |
| LOGV("TouchDex trampoline forking\n"); |
| gettimeofday(&startWhen, NULL); |
| |
| /* |
| * Retrieve strings. Note we want to do this *before* the fork() -- bad |
| * idea to perform Java operations in the child process (not all threads |
| * get carried over to the new process). |
| */ |
| bcp = env->GetStringUTFChars(bcpStr, NULL); |
| dexFiles = env->GetStringUTFChars(dexFilesStr, NULL); |
| if (bcp == NULL || dexFiles == NULL) { |
| LOGE("Bad values for bcp=%p dexFiles=%p\n", bcp, dexFiles); |
| abort(); |
| } |
| |
| pid = fork(); |
| if (pid < 0) { |
| LOGE("fork failed: %s", strerror(errno)); |
| return -1; |
| } |
| |
| if (pid == 0) { |
| /* child */ |
| char* bcpArg; |
| |
| LOGV("TouchDex trampoline in child\n"); |
| |
| bcpArg = (char*) malloc(strlen(bcp) + strlen(kBcpArgName) +1); |
| strcpy(bcpArg, kBcpArgName); |
| strcat(bcpArg, bcp); |
| |
| argv[0] = kExecFile; |
| argv[1] = bcpArg; |
| argv[2] = kVerifyArg; |
| argv[3] = kDexOptArg; |
| argv[4] = kExecMode; |
| argv[5] = kClassName; |
| argv[6] = dexFiles; |
| argv[7] = NULL; |
| |
| //LOGI("Calling execv with args:\n"); |
| //for (int i = 0; i < argc; i++) |
| // LOGI(" %d: '%s'\n", i, argv[i]); |
| |
| execv(kExecFile, (char* const*) argv); |
| free(bcpArg); |
| |
| LOGE("execv '%s' failed: %s\n", kExecFile, strerror(errno)); |
| exit(1); |
| } else { |
| int cc, count, dexCount, timeout; |
| int result = -1; |
| const char* cp; |
| |
| /* |
| * Adjust the timeout based on how many DEX files we have to |
| * process. Larger DEX files take longer, so this is a crude |
| * approximation at best. |
| * |
| * We need this for http://b/issue?id=836771, which can leave us |
| * stuck waiting for a long time even if there is no work to be done. |
| * |
| * This is currently being (ab)used to convert single files, which |
| * sort of spoils the timeout calculation. We establish a minimum |
| * timeout for single apps. |
| * |
| * The timeout calculation doesn't work at all right when a |
| * directory is specified. So the minimum is now a minute. At |
| * this point it's probably safe to just remove the timeout. |
| * |
| * The timeout is in 1/10ths of a second. |
| */ |
| dexCount = 1; |
| cp = dexFiles; |
| while (*++cp != '\0') { |
| if (*cp == ':') |
| dexCount++; |
| } |
| timeout = timeoutMult * dexCount; |
| if (timeout < kMinTimeout) |
| timeout = kMinTimeout; |
| |
| env->ReleaseStringUTFChars(bcpStr, bcp); |
| env->ReleaseStringUTFChars(dexFilesStr, dexFiles); |
| |
| |
| LOGD("TouchDex parent waiting for pid=%d (timeout=%.1fs)\n", |
| (int) pid, timeout / 10.0); |
| for (count = 0; count < timeout; count++) { |
| /* waitpid doesn't take a timeout, so poll and sleep */ |
| cc = waitpid(pid, &result, WNOHANG); |
| if (cc < 0) { |
| LOGE("waitpid(%d) failed: %s", (int) pid, strerror(errno)); |
| return -1; |
| } else if (cc == 0) { |
| usleep(100000); /* 0.1 sec */ |
| } else { |
| /* success! */ |
| break; |
| } |
| } |
| |
| if (count == timeout) { |
| /* note kill(0) returns 0 if the pid is a zombie */ |
| LOGE("timed out waiting for %d; kill(0) returns %d\n", |
| (int) pid, kill(pid, 0)); |
| logProcStatus(pid); |
| } else { |
| LOGV("TouchDex done after %d iterations (kill(0) returns %d)\n", |
| count, kill(pid, 0)); |
| } |
| |
| gettimeofday(&endWhen, NULL); |
| long long start = startWhen.tv_sec * 1000000 + startWhen.tv_usec; |
| long long end = endWhen.tv_sec * 1000000 + endWhen.tv_usec; |
| |
| LOGI("Dalvik-cache prep: status=0x%04x, finished in %dms\n", |
| result, (int) ((end - start) / 1000)); |
| |
| if (WIFEXITED(result)) |
| return WEXITSTATUS(result); |
| else |
| return result; |
| } |
| } |
| |
| /* |
| * Dump the contents of /proc/<pid>/status to the log file. |
| */ |
| static void logProcStatus(pid_t pid) |
| { |
| char localBuf[256]; |
| FILE* fp; |
| |
| sprintf(localBuf, "/proc/%d/status", (int) pid); |
| fp = fopen(localBuf, "r"); |
| if (fp == NULL) { |
| LOGI("Unable to open '%s'\n", localBuf); |
| return; |
| } |
| |
| LOGI("Contents of %s:\n", localBuf); |
| while (true) { |
| fgets(localBuf, sizeof(localBuf), fp); |
| if (ferror(fp) || feof(fp)) |
| break; |
| LOGI(" %s", localBuf); |
| } |
| |
| fclose(fp); |
| } |
| |
| static JNINativeMethod gMethods[] = { |
| { "trampoline", "(Ljava/lang/String;Ljava/lang/String;)I", |
| (void*) dalvik_system_TouchDex_trampoline }, |
| }; |
| int register_dalvik_system_TouchDex(JNIEnv* env) { |
| return jniRegisterNativeMethods(env, JAVA_PACKAGE "/TouchDex", gMethods, NELEM(gMethods)); |
| } |
| |
| }; // namespace android |