Home | History | Annotate | Download | only in native
      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