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 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