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
- Android应用优化过程分析
- android应用冷启动过程分析与优化过程
- Android 应用冷启动过程分析和优化方案
- Android 应用安装过程分析
- Android 你应该知道的的应用冷启动过程分析和优化方案
- Android 你应该知道的的应用冷启动过程分析和优化方案
- Android 你应该知道的的应用冷启动过程分析和优化方案
- Android 你应该知道的的应用冷启动过程分析和优化方案
- Android 你应该知道的的应用冷启动过程分析和优化方案
- Android应用性能优化之绘图分析
- Android应用性能优化之绘图分析
- Android应用开发性能优化完全分析
- Android应用开发性能优化完全分析
- Android应用开发性能优化完全分析
- Android应用开发性能优化完全分析
- Android应用开发性能优化完全分析
- Android应用开发性能优化完全分析
- Android应用开发性能优化完全分析
- Best Time to Buy and Sell Stock
- ROS(indigo)一个简单灵活和可扩展的2D多机器人仿真器stdr_simulator
- SGU326 perspective 最大流 竞赛排名问题
- python3 比较两个list的结构
- poj 1860 Currency Exchange
- Android应用优化过程分析
- Codeforces Round #271 (Div. 2) F 线段树+pair
- 红黑树(插入)
- [2016ACM多校] HDU5768 容斥原理 中国剩余定理
- 完全卸载oracle11g教程、Oracle11g的卸载方法和步骤
- 风靡的七个人生工具(SWOT、PDCA、6W2H、SMART、WBS、时间管理、二八原则)
- PAT 1051. Pop Sequence
- hdu 1080 Human Gene Functions( 类似最长公共子序列)
- 【POJ2155】Matrix-二维树状数组+前缀和