Home | History | Annotate | Download | only in dexopt
      1 /*
      2  * Copyright (C) 2008 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  * Command-line DEX optimization and verification entry point.
     19  *
     20  * There are two ways to launch this:
     21  * (1) From the VM.  This takes a dozen args, one of which is a file
     22  *     descriptor that acts as both input and output.  This allows us to
     23  *     remain ignorant of where the DEX data originally came from.
     24  * (2) From installd or another native application.  Pass in a file
     25  *     descriptor for a zip file, a file descriptor for the output, and
     26  *     a filename for debug messages.  Many assumptions are made about
     27  *     what's going on (verification + optimization are enabled, boot
     28  *     class path is in BOOTCLASSPATH, etc).
     29  *
     30  * There are some fragile aspects around bootclasspath entries, owing
     31  * largely to the VM's history of working on whenever it thought it needed
     32  * instead of strictly doing what it was told.  If optimizing bootclasspath
     33  * entries, always do them in the order in which they appear in the path.
     34  */
     35 #include "Dalvik.h"
     36 #include "libdex/OptInvocation.h"
     37 
     38 #include "utils/Log.h"
     39 #include "cutils/process_name.h"
     40 
     41 #include <stdlib.h>
     42 #include <stdio.h>
     43 #include <string.h>
     44 #include <assert.h>
     45 
     46 static const char* kClassesDex = "classes.dex";
     47 
     48 
     49 /*
     50  * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
     51  * up front for the DEX optimization header.
     52  */
     53 static int extractAndProcessZip(int zipFd, int cacheFd,
     54     const char* debugFileName, int isBootstrap, const char* bootClassPath,
     55     const char* dexoptFlagStr)
     56 {
     57     ZipArchive zippy;
     58     ZipEntry zipEntry;
     59     long uncompLen, modWhen, crc32;
     60     off_t dexOffset;
     61     int err;
     62     int result = -1;
     63 
     64     memset(&zippy, 0, sizeof(zippy));
     65 
     66     /* make sure we're still at the start of an empty file */
     67     if (lseek(cacheFd, 0, SEEK_END) != 0) {
     68         LOGE("DexOptZ: new cache file '%s' is not empty\n", debugFileName);
     69         goto bail;
     70     }
     71 
     72     /*
     73      * Write a skeletal DEX optimization header.  We want the classes.dex
     74      * to come just after it.
     75      */
     76     err = dexOptCreateEmptyHeader(cacheFd);
     77     if (err != 0)
     78         goto bail;
     79 
     80     /* record the file position so we can get back here later */
     81     dexOffset = lseek(cacheFd, 0, SEEK_CUR);
     82     if (dexOffset < 0)
     83         goto bail;
     84 
     85     /*
     86      * Open the zip archive, find the DEX entry.
     87      */
     88     if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
     89         LOGW("DexOptZ: unable to open zip archive '%s'\n", debugFileName);
     90         goto bail;
     91     }
     92 
     93     zipEntry = dexZipFindEntry(&zippy, kClassesDex);
     94     if (zipEntry == NULL) {
     95         LOGW("DexOptZ: zip archive '%s' does not include %s\n",
     96             debugFileName, kClassesDex);
     97         goto bail;
     98     }
     99 
    100     /*
    101      * Extract some info about the zip entry.
    102      */
    103     if (!dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
    104             &modWhen, &crc32))
    105     {
    106         LOGW("DexOptZ: zip archive GetEntryInfo failed on %s\n", debugFileName);
    107         goto bail;
    108     }
    109 
    110     uncompLen = uncompLen;
    111     modWhen = modWhen;
    112     crc32 = crc32;
    113 
    114     /*
    115      * Extract the DEX data into the cache file at the current offset.
    116      */
    117     if (!dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd)) {
    118         LOGW("DexOptZ: extraction of %s from %s failed\n",
    119             kClassesDex, debugFileName);
    120         goto bail;
    121     }
    122 
    123     /*
    124      * Prep the VM and perform the optimization.
    125      */
    126     DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
    127     DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
    128     int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */
    129     if (dexoptFlagStr[0] != '\0') {
    130         const char* opc;
    131         const char* val;
    132 
    133         opc = strstr(dexoptFlagStr, "v=");      /* verification */
    134         if (opc != NULL) {
    135             switch (*(opc+2)) {
    136             case 'n':   verifyMode = VERIFY_MODE_NONE;          break;
    137             case 'r':   verifyMode = VERIFY_MODE_REMOTE;        break;
    138             case 'a':   verifyMode = VERIFY_MODE_ALL;           break;
    139             default:                                            break;
    140             }
    141         }
    142 
    143         opc = strstr(dexoptFlagStr, "o=");      /* optimization */
    144         if (opc != NULL) {
    145             switch (*(opc+2)) {
    146             case 'n':   dexOptMode = OPTIMIZE_MODE_NONE;        break;
    147             case 'v':   dexOptMode = OPTIMIZE_MODE_VERIFIED;    break;
    148             case 'a':   dexOptMode = OPTIMIZE_MODE_ALL;         break;
    149             default:                                            break;
    150             }
    151         }
    152 
    153         opc = strstr(dexoptFlagStr, "m=y");     /* register map */
    154         if (opc != NULL) {
    155             dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
    156         }
    157     }
    158     if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
    159             dexoptFlags) != 0)
    160     {
    161         LOGE("DexOptZ: VM init failed\n");
    162         goto bail;
    163     }
    164 
    165     //vmStarted = 1;
    166 
    167     /* do the optimization */
    168     if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
    169             modWhen, crc32, isBootstrap))
    170     {
    171         LOGE("Optimization failed\n");
    172         goto bail;
    173     }
    174 
    175     /* we don't shut the VM down -- process is about to exit */
    176 
    177     result = 0;
    178 
    179 bail:
    180     dexZipCloseArchive(&zippy);
    181     return result;
    182 }
    183 
    184 
    185 /* advance to the next arg and extract it */
    186 #define GET_ARG(_var, _func, _msg)                                          \
    187     {                                                                       \
    188         char* endp;                                                         \
    189         (_var) = _func(*++argv, &endp, 0);                                  \
    190         if (*endp != '\0') {                                                \
    191             LOGE("%s '%s'", _msg, *argv);                                   \
    192             goto bail;                                                      \
    193         }                                                                   \
    194         --argc;                                                             \
    195     }
    196 
    197 /*
    198  * Parse arguments.  We want:
    199  *   0. (name of dexopt command -- ignored)
    200  *   1. "--zip"
    201  *   2. zip fd (input, read-only)
    202  *   3. cache fd (output, read-write, locked with flock)
    203  *   4. filename of file being optimized (used for debug messages and
    204  *      for comparing against BOOTCLASSPATH -- does not need to be
    205  *      accessible or even exist)
    206  *   5. dexopt flags
    207  *
    208  * The BOOTCLASSPATH environment variable is assumed to hold the correct
    209  * boot class path.  If the filename provided appears in the boot class
    210  * path, the path will be truncated just before that entry (so that, if
    211  * you were to dexopt "core.jar", your bootclasspath would be empty).
    212  *
    213  * This does not try to normalize the boot class path name, so the
    214  * filename test won't catch you if you get creative.
    215  */
    216 static int fromZip(int argc, char* const argv[])
    217 {
    218     int result = -1;
    219     int zipFd, cacheFd, vmBuildVersion;
    220     const char* inputFileName;
    221     char* bcpCopy = NULL;
    222     const char* dexoptFlagStr;
    223 
    224     if (argc != 6) {
    225         LOGE("Wrong number of args for --zip (found %d)\n", argc);
    226         goto bail;
    227     }
    228 
    229     /* skip "--zip" */
    230     argc--;
    231     argv++;
    232 
    233     GET_ARG(zipFd, strtol, "bad zip fd");
    234     GET_ARG(cacheFd, strtol, "bad cache fd");
    235     inputFileName = *++argv;
    236     --argc;
    237     dexoptFlagStr = *++argv;
    238     --argc;
    239 
    240     /*
    241      * Check to see if this is a bootstrap class entry.  If so, truncate
    242      * the path.
    243      */
    244     const char* bcp = getenv("BOOTCLASSPATH");
    245     if (bcp == NULL) {
    246         LOGE("DexOptZ: BOOTCLASSPATH not set\n");
    247         goto bail;
    248     }
    249 
    250     int isBootstrap = false;
    251     const char* match = strstr(bcp, inputFileName);
    252     if (match != NULL) {
    253         /*
    254          * TODO: we have a partial string match, but that doesn't mean
    255          * we've matched an entire path component.  We should make sure
    256          * that we're matching on the full inputFileName, and if not we
    257          * should re-do the strstr starting at (match+1).
    258          *
    259          * The scenario would be a bootclasspath with something like
    260          * "/system/framework/core.jar" while we're trying to optimize
    261          * "/framework/core.jar".  Not very likely since all paths are
    262          * absolute and end with ".jar", but not impossible.
    263          */
    264         int matchOffset = match - bcp;
    265         if (matchOffset > 0 && bcp[matchOffset-1] == ':')
    266             matchOffset--;
    267         LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
    268             inputFileName, matchOffset);
    269         bcpCopy = strdup(bcp);
    270         bcpCopy[matchOffset] = '\0';
    271 
    272         bcp = bcpCopy;
    273         LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
    274         isBootstrap = true;
    275     }
    276 
    277     result = extractAndProcessZip(zipFd, cacheFd, inputFileName,
    278                 isBootstrap, bcp, dexoptFlagStr);
    279 
    280 bail:
    281     free(bcpCopy);
    282     return result;
    283 }
    284 
    285 /*
    286  * Parse arguments for an "old-style" invocation directly from the VM.
    287  *
    288  * Here's what we want:
    289  *   0. (name of dexopt command -- ignored)
    290  *   1. "--dex"
    291  *   2. DALVIK_VM_BUILD value, as a sanity check
    292  *   3. file descriptor, locked with flock, for DEX file being optimized
    293  *   4. DEX offset within file
    294  *   5. DEX length
    295  *   6. filename of file being optimized (for debug messages only)
    296  *   7. modification date of source (goes into dependency section)
    297  *   8. CRC of source (goes into dependency section)
    298  *   9. flags (optimization level, isBootstrap)
    299  *  10. bootclasspath entry #1
    300  *  11. bootclasspath entry #2
    301  *   ...
    302  *
    303  * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
    304  * argument list and calls this executable.
    305  *
    306  * The bootclasspath entries become the dependencies for this DEX file.
    307  *
    308  * The open file descriptor MUST NOT be for one of the bootclasspath files.
    309  * The parent has the descriptor locked, and we'll try to lock it again as
    310  * part of processing the bootclasspath.  (We can catch this and return
    311  * an error by comparing filenames or by opening the bootclasspath files
    312  * and stat()ing them for inode numbers).
    313  */
    314 static int fromDex(int argc, char* const argv[])
    315 {
    316     int result = -1;
    317     bool vmStarted = false;
    318     char* bootClassPath = NULL;
    319     int fd, flags, vmBuildVersion;
    320     long offset, length;
    321     const char* debugFileName;
    322     u4 crc, modWhen;
    323     char* endp;
    324 
    325     if (argc < 10) {
    326         /* don't have all mandatory args */
    327         LOGE("Not enough arguments for --dex (found %d)\n", argc);
    328         goto bail;
    329     }
    330 
    331     /* skip "--dex" */
    332     argc--;
    333     argv++;
    334 
    335     /*
    336      * Extract the args.
    337      */
    338     GET_ARG(vmBuildVersion, strtol, "bad vm build");
    339     if (vmBuildVersion != DALVIK_VM_BUILD) {
    340         LOGE("DexOpt: build rev does not match VM: %d vs %d\n",
    341             vmBuildVersion, DALVIK_VM_BUILD);
    342         goto bail;
    343     }
    344     GET_ARG(fd, strtol, "bad fd");
    345     GET_ARG(offset, strtol, "bad offset");
    346     GET_ARG(length, strtol, "bad length");
    347     debugFileName = *++argv;
    348     --argc;
    349     GET_ARG(modWhen, strtoul, "bad modWhen");
    350     GET_ARG(crc, strtoul, "bad crc");
    351     GET_ARG(flags, strtol, "bad flags");
    352 
    353     LOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=0x%x crc=0x%x flg=%d (argc=%d)\n",
    354         fd, offset, length, debugFileName, modWhen, crc, flags, argc);
    355     assert(argc > 0);
    356 
    357     if (--argc == 0) {
    358         bootClassPath = strdup("");
    359     } else {
    360         int i, bcpLen;
    361         char* const* argp;
    362         char* cp;
    363 
    364         bcpLen = 0;
    365         for (i = 0, argp = argv; i < argc; i++) {
    366             ++argp;
    367             LOGV("DEP: '%s'\n", *argp);
    368             bcpLen += strlen(*argp) + 1;
    369         }
    370 
    371         cp = bootClassPath = (char*) malloc(bcpLen +1);
    372         for (i = 0, argp = argv; i < argc; i++) {
    373             int strLen;
    374 
    375             ++argp;
    376             strLen = strlen(*argp);
    377             if (i != 0)
    378                 *cp++ = ':';
    379             memcpy(cp, *argp, strLen);
    380             cp += strLen;
    381         }
    382         *cp = '\0';
    383 
    384         assert((int) strlen(bootClassPath) == bcpLen-1);
    385     }
    386     LOGV("  bootclasspath is '%s'\n", bootClassPath);
    387 
    388     /* start the VM partway */
    389     bool onlyOptVerifiedDex = false;
    390     DexClassVerifyMode verifyMode;
    391     DexOptimizerMode dexOptMode;
    392     int dexoptFlags = 0;
    393 
    394     /* ugh -- upgrade these to a bit field if they get any more complex */
    395     if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
    396         if ((flags & DEXOPT_VERIFY_ALL) != 0)
    397             verifyMode = VERIFY_MODE_ALL;
    398         else
    399             verifyMode = VERIFY_MODE_REMOTE;
    400     } else {
    401         verifyMode = VERIFY_MODE_NONE;
    402     }
    403     if ((flags & DEXOPT_OPT_ENABLED) != 0) {
    404         if ((flags & DEXOPT_OPT_ALL) != 0)
    405             dexOptMode = OPTIMIZE_MODE_ALL;
    406         else
    407             dexOptMode = OPTIMIZE_MODE_VERIFIED;
    408     } else {
    409         dexOptMode = OPTIMIZE_MODE_NONE;
    410     }
    411     if ((flags & DEXOPT_GEN_REGISTER_MAP) != 0) {
    412         dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
    413     }
    414 
    415     if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
    416             dexoptFlags) != 0)
    417     {
    418         LOGE("VM init failed\n");
    419         goto bail;
    420     }
    421 
    422     vmStarted = true;
    423 
    424     /* do the optimization */
    425     if (!dvmContinueOptimization(fd, offset, length, debugFileName,
    426             modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
    427     {
    428         LOGE("Optimization failed\n");
    429         goto bail;
    430     }
    431 
    432     result = 0;
    433 
    434 bail:
    435     /*
    436      * In theory we should gracefully shut the VM down at this point.  In
    437      * practice that only matters if we're checking for memory leaks with
    438      * valgrind -- simply exiting is much faster.
    439      *
    440      * As it turns out, the DEX optimizer plays a little fast and loose
    441      * with class loading.  We load all of the classes from a partially-
    442      * formed DEX file, which is unmapped when we're done.  If we want to
    443      * do clean shutdown here, perhaps for testing with valgrind, we need
    444      * to skip the munmap call there.
    445      */
    446 #if 0
    447     if (vmStarted) {
    448         LOGI("DexOpt shutting down, result=%d\n", result);
    449         dvmShutdown();
    450     }
    451 #endif
    452 
    453     //dvmLinearAllocDump(NULL);
    454 
    455 #if 0
    456     {
    457         extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData,
    458                gDvm__gcSimpleData;
    459         LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n",
    460             gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData);
    461     }
    462 #endif
    463 
    464     free(bootClassPath);
    465     LOGV("DexOpt command complete (result=%d)\n", result);
    466     return result;
    467 }
    468 
    469 /*
    470  * Main entry point.  Decide where to go.
    471  */
    472 int main(int argc, char* const argv[])
    473 {
    474     set_process_name("dexopt");
    475 
    476     setvbuf(stdout, NULL, _IONBF, 0);
    477 
    478     if (argc > 1) {
    479         if (strcmp(argv[1], "--zip") == 0)
    480             return fromZip(argc, argv);
    481         else if (strcmp(argv[1], "--dex") == 0)
    482             return fromDex(argc, argv);
    483     }
    484 
    485     fprintf(stderr, "Usage: don't use this\n");
    486     return 1;
    487 }
    488 
    489