Android应用优化过程分析

来源:互联网 发布:淘宝网q币充值软件 编辑:程序博客网 时间:2024/06/06 06:57

最近在看其他的源代码,Android的源码几天没看了。

假期的计划总赶不上变化,尴尬。。。

接着Android应用的安装,看看优化(odex)的过程源码

上篇讲到在installd.c当中,命令参数与cmds中保存的命令进行匹配。

cmds中保存的命令如下

struct cmdinfo {    const char *name;    unsigned numargs;    int (*func)(char **arg, char reply[REPLY_MAX]);};struct cmdinfo cmds[] = {    { "ping",                 0, do_ping },    { "install",              4, do_install },    { "dexopt",               3, do_dexopt },    { "movedex",              2, do_move_dex },    { "rmdex",                1, do_rm_dex },    { "remove",               2, do_remove },    { "rename",               2, do_rename },    { "fixuid",               3, do_fixuid },    { "freecache",            1, do_free_cache },    { "rmcache",              2, do_rm_cache },    { "getsize",              6, do_get_size },    { "rmuserdata",           2, do_rm_user_data },    { "movefiles",            0, do_movefiles },    { "linklib",              3, do_linklib },    { "mkuserdata",           3, do_mk_user_data },    { "rmuser",               1, do_rm_user },};
其中,dexopt命令就是应用优化命令

do_dexopt函数

static int do_dexopt(char **arg, char reply[REPLY_MAX]){        /* apk_path, uid, is_public */    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]));}
dexopt函数位于同目录下的commands.c文件中

int dexopt(const char *apk_path, uid_t uid, int is_public){    struct utimbuf ut;    struct stat apk_stat, dex_stat;    char dex_path[PKG_PATH_MAX];    char dexopt_flags[PROPERTY_VALUE_MAX];    char *end;    int res, zip_fd=-1, odex_fd=-1;        /* Before anything else: is there a .odex file?  If so, we have         * pre-optimized the apk and there is nothing to do here.         */    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {        return -1;    }    /* platform-specific flags affecting optimization and verification */    property_get("dalvik.vm.dexopt-flags", dexopt_flags, "");    strcpy(dex_path, apk_path);    end = strrchr(dex_path, '.');    if (end != NULL) {        strcpy(end, ".odex");        if (stat(dex_path, &dex_stat) == 0) {            return 0;        }    }    if (create_cache_path(dex_path, apk_path)) {        return -1;    }    memset(&apk_stat, 0, sizeof(apk_stat));    stat(apk_path, &apk_stat);    zip_fd = open(apk_path, O_RDONLY, 0);    if (zip_fd < 0) {        ALOGE("dexopt cannot open '%s' for input\n", apk_path);        return -1;    }    unlink(dex_path);    odex_fd = open(dex_path, O_RDWR | O_CREAT | O_EXCL, 0644);    if (odex_fd < 0) {        ALOGE("dexopt cannot open '%s' for output\n", dex_path);        goto fail;    }    if (fchmod(odex_fd,               S_IRUSR|S_IWUSR|S_IRGRP |               (is_public ? S_IROTH : 0)) < 0) {        ALOGE("dexopt cannot chmod '%s'\n", dex_path);        goto fail;    }    if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {        ALOGE("dexopt cannot chown '%s'\n", dex_path);        goto fail;    }    ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);    pid_t pid;    pid = fork();    if (pid == 0) {        /* child -- drop privileges before continuing */        if (setgid(uid) != 0) {            ALOGE("setgid(%d) failed during dexopt\n", uid);            exit(64);        }        if (setuid(uid) != 0) {            ALOGE("setuid(%d) during dexopt\n", uid);            exit(65);        }        // drop capabilities        struct __user_cap_header_struct capheader;        struct __user_cap_data_struct capdata[2];        memset(&capheader, 0, sizeof(capheader));        memset(&capdata, 0, sizeof(capdata));        capheader.version = _LINUX_CAPABILITY_VERSION_3;        if (capset(&capheader, &capdata[0]) < 0) {            ALOGE("capset failed: %s\n", strerror(errno));            exit(66);        }        if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) {            ALOGE("flock(%s) failed: %s\n", dex_path, strerror(errno));            exit(67);        }        run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags);        exit(68);   /* only get here on exec failure */    } else {        res = wait_dexopt(pid, apk_path);        if (res != 0) {            ALOGE("dexopt failed on '%s' res = %d\n", dex_path, res);            goto fail;        }    }    ut.actime = apk_stat.st_atime;    ut.modtime = apk_stat.st_mtime;    utime(dex_path, &ut);        close(odex_fd);    close(zip_fd);    return 0;fail:    if (odex_fd >= 0) {        close(odex_fd);        unlink(dex_path);    }    if (zip_fd >= 0) {        close(zip_fd);    }    return -1;}
构造路径,打开原文件,打开(没有就创建)odex文件,然后fork一个进程执行优化

主要的优化在run_dexopt函数中

static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,    const char* dexopt_flags){    static const char* DEX_OPT_BIN = "/system/bin/dexopt";    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig    char zip_num[MAX_INT_LEN];    char odex_num[MAX_INT_LEN];    sprintf(zip_num, "%d", zip_fd);    sprintf(odex_num, "%d", odex_fd);    execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,        dexopt_flags, (char*) NULL);    ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));}
用execl执行/system/bin/dexopt

/* * Main entry point.  Decide where to go. */int main(int argc, char* const argv[]){    set_process_name("dexopt");    setvbuf(stdout, NULL, _IONBF, 0);    if (argc > 1) {        if (strcmp(argv[1], "--zip") == 0)            return fromZip(argc, argv);        else if (strcmp(argv[1], "--dex") == 0)            return fromDex(argc, argv);        else if (strcmp(argv[1], "--preopt") == 0)            return preopt(argc, argv);    }    fprintf(stderr,        "Usage:\n\n"        "Short version: Don't use this.\n\n"        "Slightly longer version: This system-internal tool is used to\n"        "produce optimized dex files. See the source code for details.\n");    return 1;}
根据"--zip",执行fromZip

static int fromZip(int argc, char* const argv[]){    int result = -1;    int zipFd, cacheFd;    const char* zipName;    char* bcpCopy = NULL;    const char* dexoptFlags;    if (argc != 6) {        ALOGE("Wrong number of args for --zip (found %d)", argc);        goto bail;    }    /* skip "--zip" */    argc--;    argv++;    GET_ARG(zipFd, strtol, "bad zip fd");    GET_ARG(cacheFd, strtol, "bad cache fd");    zipName = *++argv;    --argc;    dexoptFlags = *++argv;    --argc;    result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);bail:    return result;}
GET_ARG是个同文件下的宏定义

/* advance to the next arg and extract it */#define GET_ARG(_var, _func, _msg)                                          \    {                                                                       \        char* endp;                                                         \        (_var) = _func(*++argv, &endp, 0);                                  \        if (*endp != '\0') {                                                \            ALOGE("%s '%s'", _msg, *argv);                                   \            goto bail;                                                      \        }                                                                   \        --argc;                                                             \    }
函数的最后,也是核心,调用processZipFile

/* * Common functionality for normal device-side processing as well as * preoptimization. */static int processZipFile(int zipFd, int cacheFd, const char* zipName,        const char *dexoptFlags){    char* bcpCopy = NULL;    /*     * Check to see if this is a bootstrap class entry. If so, truncate     * the path.     */    const char* bcp = getenv("BOOTCLASSPATH");    if (bcp == NULL) {        ALOGE("DexOptZ: BOOTCLASSPATH not set");        return -1;    }    bool isBootstrap = false;    const char* match = strstr(bcp, zipName);    if (match != NULL) {        /*         * TODO: we have a partial string match, but that doesn't mean         * we've matched an entire path component. We should make sure         * that we're matching on the full zipName, and if not we         * should re-do the strstr starting at (match+1).         *         * The scenario would be a bootclasspath with something like         * "/system/framework/core.jar" while we're trying to optimize         * "/framework/core.jar". Not very likely since all paths are         * absolute and end with ".jar", but not impossible.         */        int matchOffset = match - bcp;        if (matchOffset > 0 && bcp[matchOffset-1] == ':')            matchOffset--;        ALOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d",            zipName, matchOffset);        bcpCopy = strdup(bcp);        bcpCopy[matchOffset] = '\0';        bcp = bcpCopy;        ALOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'", bcp);        isBootstrap = true;    }    int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,            bcp, dexoptFlags);    free(bcpCopy);    return result;}
processZipFile进行包装验证后调用extractAndProcessZip

/* * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space * up front for the DEX optimization header. */static int extractAndProcessZip(int zipFd, int cacheFd,    const char* debugFileName, bool isBootstrap, const char* bootClassPath,    const char* dexoptFlagStr){    ZipArchive zippy;    ZipEntry zipEntry;    size_t uncompLen;    long modWhen, crc32;    off_t dexOffset;    int err;    int result = -1;    int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */    DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;    DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;    memset(&zippy, 0, sizeof(zippy));    /* make sure we're still at the start of an empty file */    if (lseek(cacheFd, 0, SEEK_END) != 0) {        ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName);        goto bail;    }    /*     * Write a skeletal DEX optimization header.  We want the classes.dex     * to come just after it.     */    err = dexOptCreateEmptyHeader(cacheFd);    if (err != 0)        goto bail;    /* record the file position so we can get back here later */    dexOffset = lseek(cacheFd, 0, SEEK_CUR);    if (dexOffset < 0)        goto bail;    /*     * Open the zip archive, find the DEX entry.     */    if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {        ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);        goto bail;    }    zipEntry = dexZipFindEntry(&zippy, kClassesDex);    if (zipEntry == NULL) {        ALOGW("DexOptZ: zip archive '%s' does not include %s",            debugFileName, kClassesDex);        goto bail;    }    /*     * Extract some info about the zip entry.     */    if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,            &modWhen, &crc32) != 0)    {        ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName);        goto bail;    }    uncompLen = uncompLen;    modWhen = modWhen;    crc32 = crc32;    /*     * Extract the DEX data into the cache file at the current offset.     */    if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {        ALOGW("DexOptZ: extraction of %s from %s failed",            kClassesDex, debugFileName);        goto bail;    }    /* Parse the options. */    if (dexoptFlagStr[0] != '\0') {        const char* opc;        const char* val;        opc = strstr(dexoptFlagStr, "v=");      /* verification */        if (opc != NULL) {            switch (*(opc+2)) {            case 'n':   verifyMode = VERIFY_MODE_NONE;          break;            case 'r':   verifyMode = VERIFY_MODE_REMOTE;        break;            case 'a':   verifyMode = VERIFY_MODE_ALL;           break;            default:                                            break;            }        }        opc = strstr(dexoptFlagStr, "o=");      /* optimization */        if (opc != NULL) {            switch (*(opc+2)) {            case 'n':   dexOptMode = OPTIMIZE_MODE_NONE;        break;            case 'v':   dexOptMode = OPTIMIZE_MODE_VERIFIED;    break;            case 'a':   dexOptMode = OPTIMIZE_MODE_ALL;         break;            case 'f':   dexOptMode = OPTIMIZE_MODE_FULL;        break;            default:                                            break;            }        }        opc = strstr(dexoptFlagStr, "m=y");     /* register map */        if (opc != NULL) {            dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;        }        opc = strstr(dexoptFlagStr, "u=");      /* uniprocessor target */        if (opc != NULL) {            switch (*(opc+2)) {            case 'y':   dexoptFlags |= DEXOPT_UNIPROCESSOR;     break;            case 'n':   dexoptFlags |= DEXOPT_SMP;              break;            default:                                            break;            }        }    }    /*     * Prep the VM and perform the optimization.     */    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,            dexoptFlags) != 0)    {        ALOGE("DexOptZ: VM init failed");        goto bail;    }    //vmStarted = 1;    /* do the optimization */    if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,            modWhen, crc32, isBootstrap))    {        ALOGE("Optimization failed");        goto bail;    }    /* we don't shut the VM down -- process is about to exit */    result = 0;bail:    dexZipCloseArchive(&zippy);    return result;}
这个函数可以很明显的看出来分为几段,首先构造一个空文件和空文件头,将原来文件中的部分数据拷贝到空文件中。

其次,解析设置参数(Parse the options)

最后准备和执行优化,dvmPrepForDexOpt和dvmContinueOptimization函数

重点看后一个dvmContinueOptimization函数

该函数位于dalvik\vm\analysis\DexPrepare.cpp中

函数很长,分段来看

bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap){    DexClassLookup* pClassLookup = NULL;    RegisterMapBuilder* pRegMapBuilder = NULL;    assert(gDvm.optimizing);    ALOGV("Continuing optimization (%s, isb=%d)", fileName, isBootstrap);    assert(dexOffset >= 0);    /* quick test so we don't blow up on empty file */    if (dexLength < (int) sizeof(DexHeader)) {        ALOGE("too small to be DEX");        return false;    }    if (dexOffset < (int) sizeof(DexOptHeader)) {        ALOGE("not enough room for opt header");        return false;    }    bool result = false;
声明变量,检查长度

    /*     * Drop this into a global so we don't have to pass it around.  We could     * also add a field to DexFile, but since it only pertains to DEX     * creation that probably doesn't make sense.     */    gDvm.optimizingBootstrapClass = isBootstrap;    {        /*         * Map the entire file (so we don't have to worry about page         * alignment).  The expectation is that the output file contains         * our DEX data plus room for a small header.         */        bool success;        void* mapAddr;        mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,                    MAP_SHARED, fd, 0);        if (mapAddr == MAP_FAILED) {            ALOGE("unable to mmap DEX cache: %s", strerror(errno));            goto bail;        }        bool doVerify, doOpt;        if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {            doVerify = false;        } else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {            doVerify = !gDvm.optimizingBootstrapClass;        } else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {            doVerify = true;        }        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {            doOpt = false;        } else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||                   gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {            doOpt = doVerify;        } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {            doOpt = true;        }
将文件内容mmap到mapAddr,根据从全局变量gDvm传递的信息设置选项

        /*         * Rewrite the file.  Byte reordering, structure realigning,         * class verification, and bytecode optimization are all performed         * here.         *         * In theory the file could change size and bits could shift around.         * In practice this would be annoying to deal with, so the file         * layout is designed so that it can always be rewritten in place.         *         * This creates the class lookup table as part of doing the processing.         */        success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,                    doVerify, doOpt, &pClassLookup, NULL);        if (success) {            DvmDex* pDvmDex = NULL;            u1* dexAddr = ((u1*) mapAddr) + dexOffset;            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {                ALOGE("Unable to create DexFile");                success = false;            } else {                /*                 * If configured to do so, generate register map output                 * for all verified classes.  The register maps were                 * generated during verification, and will now be serialized.                 */                if (gDvm.generateRegisterMaps) {                    pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);                    if (pRegMapBuilder == NULL) {                        ALOGE("Failed generating register maps");                        success = false;                    }                }                DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;                updateChecksum(dexAddr, dexLength, pHeader);                dvmDexFileFree(pDvmDex);            }        }
从原文注释就可以看到,这段很关键,文件重写,比特位、结构重排,类核实,字节码优化都在这部分

        /* unmap the read-write version, forcing writes to disk */        if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {            ALOGW("msync failed: %s", strerror(errno));            // weird, but keep going        }#if 1        /*         * This causes clean shutdown to fail, because we have loaded classes         * that point into it.  For the optimizer this isn't a problem,         * because it's more efficient for the process to simply exit.         * Exclude this code when doing clean shutdown for valgrind.         */        if (munmap(mapAddr, dexOffset + dexLength) != 0) {            ALOGE("munmap failed: %s", strerror(errno));            goto bail;        }#endif        if (!success)            goto bail;    }
这段没什么好说的

    /* get start offset, and adjust deps start for 64-bit alignment */    off_t depsOffset, optOffset, endOffset, adjOffset;    int depsLength, optLength;    u4 optChecksum;    depsOffset = lseek(fd, 0, SEEK_END);    if (depsOffset < 0) {        ALOGE("lseek to EOF failed: %s", strerror(errno));        goto bail;    }    adjOffset = (depsOffset + 7) & ~(0x07);    if (adjOffset != depsOffset) {        ALOGV("Adjusting deps start from %d to %d",            (int) depsOffset, (int) adjOffset);        depsOffset = adjOffset;        lseek(fd, depsOffset, SEEK_SET);    }    /*     * Append the dependency list.     */    if (writeDependencies(fd, modWhen, crc) != 0) {        ALOGW("Failed writing dependencies");        goto bail;    }    /* compute deps length, then adjust opt start for 64-bit alignment */    optOffset = lseek(fd, 0, SEEK_END);    depsLength = optOffset - depsOffset;    adjOffset = (optOffset + 7) & ~(0x07);    if (adjOffset != optOffset) {        ALOGV("Adjusting opt start from %d to %d",            (int) optOffset, (int) adjOffset);        optOffset = adjOffset;        lseek(fd, optOffset, SEEK_SET);    }    /*     * Append any optimized pre-computed data structures.     */    if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {        ALOGW("Failed writing opt data");        goto bail;    }    endOffset = lseek(fd, 0, SEEK_END);    optLength = endOffset - optOffset;    /* compute checksum from start of deps to end of opt area */    if (!computeFileChecksum(fd, depsOffset,            (optOffset+optLength) - depsOffset, &optChecksum))    {        goto bail;    }    /*     * Output the "opt" header with all values filled in and a correct     * magic number.     */    DexOptHeader optHdr;    memset(&optHdr, 0xff, sizeof(optHdr));    memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);    memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);    optHdr.dexOffset = (u4) dexOffset;    optHdr.dexLength = (u4) dexLength;    optHdr.depsOffset = (u4) depsOffset;    optHdr.depsLength = (u4) depsLength;    optHdr.optOffset = (u4) optOffset;    optHdr.optLength = (u4) optLength;#if __BYTE_ORDER != __LITTLE_ENDIAN    optHdr.flags = DEX_OPT_FLAG_BIG;#else    optHdr.flags = 0;#endif    optHdr.checksum = optChecksum;    fsync(fd);      /* ensure previous writes go before header is written */    lseek(fd, 0, SEEK_SET);    if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)        goto bail;    ALOGV("Successfully wrote DEX header");    result = true;    //dvmRegisterMapDumpStats();bail:    dvmFreeRegisterMapBuilder(pRegMapBuilder);    free(pClassLookup);    return result;}
这段有几个关键,writeDependencies添加依赖列表,writeOptData写入预计算优化信息(类索引、寄存器映射)

然后对表头进行修改

整个优化后的odex文件详见我老大的博客http://blog.csdn.net/roland_sun/article/details/47183119

最后借我老大的图一用














0 0
原创粉丝点击