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