Android ART Runtime (2) – dex2oat

篇文章中我们介绍了 Android 4.4 新开发的运行时 ART 项目,其中的一个重要模快是 dex2oat,简单讲就是使用 LLVM 把 dex 文件编译成 oat 文件(Optimized ART?)。下面我们详细研究一下 dex2oat 的功能,以及他是如何被调用的。

一、dex2oat 简介

dex2oat 顾名思义 dex file to oat file,就是在新旧两种运行时文件的转换。他是一个可执行 ELF 文件,adb 连接手机以后也可以直接调用。dex2oat 的源码只有一个文件 /art/dex2oat/dex2oat.cc。dex2oat 有很多参数,并且实用方法打印到 logcat里面,看起来非常不便,我们直接看源码 Usage()。我挑了几个比较重要的参数看了一下,先介绍一下,调用过程中可能会用到。

dex2oat 用法

  • --dex-file=: specifies a .dex file to compile.: 需要转换的 dex 文件,也可以是 apk, jar,dex2oat 会找到里面的 classes.dex 进行转换。
  • --oat-file=: specifies the oat output destination via a filename.: 指定输出 oat 的文件名。
  • --boot-image=: provide the image file for the boot class path.: 系统运行时工具类在 ART 下编译后的文件,他的例子是指向/system/framework/boot.art。但其实 boot.art 不在这个目录下,而是在/data/dalvik-cache/system@framework@boot.oat。后面还会详细介绍这个 boot 文件。
  • --compiler-backend=(Quick|QuickGBC|Portable): select compiler backend": dex2oat 好像只处理了 Quick 和 Portable 两种编译 backend,暂时还不理解有什么区别,待以后继续研究。

其他参数大多都能从他的描述中知道用途,等以后用到的时候再详细看。

oat 文件在哪里?
我们知道选择 ART 作为运行时后,需要重启手机。然后系统会使用 dex2oat 把所有 App 编译成 oat 文件。那么 oat 都存在哪里?我怎么找不到?通过查看代码和开机的 log,发现 oat 文件原来在 /data/dalvik-cache/*@classes.dex。竟然和 odex 文件名字一样!检验一下文件格式:


Shell $ file system@app@Email.apk@classes.dex system@app@Email.apk@classes.dex: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (GNU/Linux), dynamically linked, stripped

1 2 $ file system @ app @ Email .apk @ classes .dex system @ app @ Email .apk @ classes .dex : ELF 32 bit LSB shared object , ARM , EABI5 version 1 ( GNU / Linux ) , dynamically linked , stripped

果然是 ELF 文件。

二、调用过程分析

PackageManagerService 是负责 apk 包管理的服务,包括安装,检查,删除,更新等等所有与 apk 相关的服务。我在源码中研究了一下 dex2oat 详细的调用过程,在下面介绍过程的时候我打算从 PackageManagerService 入手,经过一系列的调用过程才真正执行 dex2oat。

PackageManagerService (PMS)
我们知道 PMS 服务启动之后会监控某些目录(/data/data/ 等)。如果目录多了某个文件,PMS 会调用一些方法对文件进行处理。扫描监控目录的方法是:scanDirLI(),在 PackageManagerService 的构造方法中调用,详见代码 /frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
Java // Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver( frameworkDir.getPath(), OBSERVER_EVENTS, true, false); mFrameworkInstallObserver.startWatching(); scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_NO_DEX, 0); // Collected privileged system packages. File privilegedAppDir = new File(Environment.getRootDirectory(), “priv-app”); mPrivilegedInstallObserver = new AppDirObserver( privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true); mPrivilegedInstallObserver.startWatching(); scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0); // Collect ordinary system packages. File systemAppDir = new File(Environment.getRootDirectory(), “app”); mSystemInstallObserver = new AppDirObserver( systemAppDir.getPath(), OBSERVER_EVENTS, true, false); mSystemInstallObserver.startWatching(); scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); // Collect all vendor packages. File vendorAppDir = new File(“/vendor/app”); mVendorInstallObserver = new AppDirObserver( vendorAppDir.getPath(), OBSERVER_EVENTS, true, false); mVendorInstallObserver.startWatching(); scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 // Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver (      frameworkDir . getPath ( ) , OBSERVER_EVENTS , true , false ) ; mFrameworkInstallObserver . startWatching ( ) ; scanDirLI ( frameworkDir , PackageParser . PARSE_IS_SYSTEM          | PackageParser . PARSE_IS_SYSTEM_DIR ,          scanMode | SCAN_NO_DEX , 0 ) ;   // Collected privileged system packages. File privilegedAppDir = new File ( Environment . getRootDirectory ( ) , “priv-app” ) ; mPrivilegedInstallObserver = new AppDirObserver (          privilegedAppDir . getPath ( ) , OBSERVER_EVENTS , true , true ) ; mPrivilegedInstallObserver . startWatching ( ) ;      scanDirLI ( privilegedAppDir , PackageParser . PARSE_IS_SYSTEM              | PackageParser . PARSE_IS_SYSTEM_DIR              | PackageParser . PARSE_IS_PRIVILEGED , scanMode , 0 ) ;   // Collect ordinary system packages. File systemAppDir = new File ( Environment . getRootDirectory ( ) , “app” ) ; mSystemInstallObserver = new AppDirObserver (      systemAppDir . getPath ( ) , OBSERVER_EVENTS , true , false ) ; mSystemInstallObserver . startWatching ( ) ; scanDirLI ( systemAppDir , PackageParser . PARSE_IS_SYSTEM          | PackageParser . PARSE_IS_SYSTEM_DIR , scanMode , 0 ) ;   // Collect all vendor packages. File vendorAppDir = new File ( “/vendor/app” ) ; mVendorInstallObserver = new AppDirObserver (      vendorAppDir . getPath ( ) , OBSERVER_EVENTS , true , false ) ; mVendorInstallObserver . startWatching ( ) ; scanDirLI ( vendorAppDir , PackageParser . PARSE_IS_SYSTEM          | PackageParser . PARSE_IS_SYSTEM_DIR , scanMode , 0 ) ;

从源码中可以看到,有四个地方调用 scanDirLI()。再调用之前有一个 AppDirObserver 类引起了我的注意,看一下是怎么对目录进行监控的。依然是在 PackageManagerService 中找到了 AppDirObserver 继承于 FileObserver。FileObserver 有一个 native 方法 startWatching 找到了他的实现 /frameworks/base/core/java/android/os/FileObserver.java

/frameworks/base/core/java/android/os/FileObserver.java
C const char* path = env->GetStringUTFChars(pathString, NULL); res = inotify_add_watch(fd, path, mask); env->ReleaseStringUTFChars(pathString, path);

1 2 3 const char * path = env -> GetStringUTFChars ( pathString , NULL ) ; res = inotify_add_watch ( fd , path , mask ) ; env -> ReleaseStringUTFChars ( pathString , path ) ;

man 一下 inotify_add_watch:

INOTIFY_ADD_WATCH(2) Linux Programmer’s Manual INOTIFY_ADD_WATCH(2) NAME inotify_add_watch – add a watch to an initialized inotify instance SYNOPSIS #include <sys/inotify.h> int inotify_add_watch(int fd, const char *pathname, uint32_t mask); DESCRIPTION inotify_add_watch() adds a new watch, or modifies an existing watch, for the file whose location is specified in pathname; the caller must have read permission for this file. The fd argument is a file descrip‐ tor referring to the inotify instance whose watch list is to be modi‐ fied. The events to be monitored for pathname are specified in the mask bit-mask argument. See inotify(7) for a description of the bits that can be set in mask. …

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 INOTIFY_ADD_WATCH ( 2 )        Linux Programmers Manual       INOTIFY_ADD_WATCH ( 2 )       NAME        inotify_add_watch add a watch to an initialized inotify instance   SYNOPSIS        #include <sys/inotify.h>          int inotify_add_watch ( int fd , const char * pathname , uint32_t mask ) ;   DESCRIPTION        inotify_add_watch ( )    adds    a    new watch , or modifies an existing watch ,        for the file whose location is specified in pathname ; the   caller   must        have read permission for this file .    The fd argument is a file descrip       tor referring to the inotify instance whose watch list is to    be   modi       fied .    The   events   to    be monitored for pathname are specified in the        mask bit mask argument .    See inotify ( 7 ) for a description of   the   bits        that can be set in mask . . . .

原来 inotify_add_watch() 是监控目录的。

PackageManagerService.scanDirLI()

回到正题,我们从构造方法中找到了 scanDirLI(),现在来看一下 scanDirLI() 都做了什么:

/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
Java for (i=0; i<files.length; i++) { File file = new File(dir, files[i]); if (!isPackageFilename(files[i])) { // Ignore entries which are not apk’s continue; } PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);

3458 3459 3460 3461 3462 3463 3464 3465 for ( i = 0 ; i < files . length ; i ++ ) {      File file = new File ( dir , files [ i ] ) ;      if ( ! isPackageFilename ( files [ i ] ) ) {          // Ignore entries which are not apk’s          continue ;      }         PackageParser . Package pkg = scanPackageLI ( file ,              flags | PackageParser . PARSE_MUST_BE_APK , scanMode , currentTime , null ) ;

scanDirLI() 便利所有目录下的 apk 文件并调用 scanPackageLI(),我们继续。

PackageManagerService.scanPackageLI()

scanPackageLI() 先对文件的设置进行读取,还包含其他处理。我们跳过直接看

/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
Java // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);

3708 3709 3710 // Note that we invoke the following method only if we are about to unpack an application PackageParser . Package scannedPkg = scanPackageLI ( pkg , parseFlags , scanMode        | SCAN_UPDATE_SIGNATURE , currentTime , user ) ;

又调用了另外一个重载的 scanPackageLI()。在这个 scanPackageLI() 里面又进行了一系列的包的解析,都与我们的目标无关,直接跳到这里:

/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
Java if ((scanMode&SCAN_NO_DEX) == 0) { if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } }

4610 4611 4612 4613 4614 4615 4616 if ( ( scanMode & SCAN_NO_DEX ) == 0 ) {      if ( performDexOptLI ( pkg , forceDex , ( scanMode & SCAN_DEFER_DEX ) != 0 , false )              == DEX_OPT_FAILED ) {          mLastScanError = PackageManager . INSTALL_FAILED_DEXOPT ;          return null ;      } }

在这里又调用 performDexOptLI() 来处理进行 dex 优化,参数就是这个 package。这里已经感觉到有点快到 dex2oat 的调用点了。我们继续看 performDexOptLI()。

PackageManagerService.performDexOptLI()

performDexOptLI 先检查 dex 是否需要优化,然后再有一些判断,忽略他,看到这里:

/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
Java if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { if (!forceDex && defer) { if (mDeferredDexOpt == null) { mDeferredDexOpt = new HashSet<PackageParser.Package>(); } mDeferredDexOpt.add(pkg); return DEX_OPT_DEFERRED; } else { Log.i(TAG, “Running dexopt on: ” + pkg.applicationInfo.packageName); final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo. uid); ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg)); pkg.mDidDexOpt = true; performed = true; } }

3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 if ( forceDex || dalvik . system . DexFile . isDexOptNeeded ( path ) ) {      if ( ! forceDex && defer ) {          if ( mDeferredDexOpt == null ) {              mDeferredDexOpt = new HashSet < PackageParser . Package > ( ) ;          }          mDeferredDexOpt . add ( pkg ) ;          return DEX_OPT_DEFERRED ;      } else {          Log . i ( TAG , “Running dexopt on: “ + pkg . applicationInfo . packageName ) ;          final int sharedGid = UserHandle . getSharedAppGid ( pkg . applicationInfo .    uid ) ;          ret = mInstaller . dexopt ( path , sharedGid , ! isForwardLocked ( pkg ) ) ;          pkg . mDidDexOpt = true ;          performed = true ;      } }

最后 mInstaller.dexopt 调用的优化 dex 的函数,Google 在这里把 dex2oat 和 dex2odex 统称为 dexopt。mInstaller 是 Installer 类。下面我们详细了解一下 Installer 的运行机制。

Installer

从 Installer 的代码中我们可以看到 connect(), disconnect(), readBytes(), writeCommand() 等方法。在 connect() 方法中可以看到,Installer 就是与 installd daemon 进行通信的辅助类。代码在这里:

/frameworks/base/services/java/com/android/server/pm/Installer.java
Java mSocket = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress(“installd”, LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream();

49 50 51 52 53 54 55 56 57 mSocket = new LocalSocket ( ) ;   LocalSocketAddress address = new LocalSocketAddress ( “installd” ,          LocalSocketAddress . Namespace . RESERVED ) ;   mSocket . connect ( address ) ;   mIn = mSocket . getInputStream ( ) ; mOut = mSocket . getOutputStream ( ) ;

了解了这个之后,我们找一下 dexopt 看看 Installer 向 installd 发送了什么信息:

/frameworks/base/services/java/com/android/server/pm/Installer.java
Java public int dexopt(String apkPath, int uid, boolean isPublic) { StringBuilder builder = new StringBuilder(“dexopt”); builder.append(‘ ‘); builder.append(apkPath); builder.append(‘ ‘); builder.append(uid); builder.append(isPublic ? ” 1″ : ” 0″); return execute(builder.toString()); }

204 205 206 207 208 209 210 211 212 public int dexopt ( String apkPath , int uid , boolean isPublic ) {      StringBuilder builder = new StringBuilder ( “dexopt” ) ;      builder . append ( ‘ ‘ ) ;      builder . append ( apkPath ) ;      builder . append ( ‘ ‘ ) ;      builder . append ( uid ) ;      builder . append ( isPublic ? ” 1″ : ” 0″ ) ;      return execute ( builder . toString ( ) ) ; }

其实发送了 dexopt apkpath uid 1|0 给 installd。好了,Installer 分析结束,我们转战 installd。

installd.c

installd 是 socket 通信的 server,会 accept 连接,并读取数据。从下面的两个死循环就能看出来:

/frameworks/native/cmds/installd/installd.c
C for (;;) { alen = sizeof(addr); s = accept(lsocket, &addr, &alen); if (s < 0) { ALOGE(“Accept failed: %s\n”, strerror(errno)); continue; } fcntl(s, F_SETFD, FD_CLOEXEC); ALOGI(“new connection\n”); for (;;) { unsigned short count; if (readx(s, &count, sizeof(count))) {

554 555 556 557 558 559 560 561 562 563 564 565 566 for ( ; ; ) {      alen = sizeof ( addr ) ;      s = accept ( lsocket , & addr , & alen ) ;      if ( s < 0 ) {          ALOGE ( “Accept failed: %s\n” , strerror ( errno ) ) ;          continue ;      }         fcntl ( s , F_SETFD , FD_CLOEXEC ) ;        ALOGI ( “new connection\n” ) ;      for ( ; ; ) {          unsigned short count ;          if ( readx ( s , & count , sizeof ( count ) ) ) {

之后会调用 execute 函数:

/frameworks/native/cmds/installd/installd.c
C if (execute(s, buf)) break;

1 if ( execute ( s , buf ) ) break ;

execute() 函数先把 cmd 分割然后通过 cmds 表查询函数名称:

/frameworks/native/cmds/installd/installd.c
C 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 }, };

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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() 函数,do_dexopt() 又调用了 commands.c 里面的 dexopt() 函数。感觉离我们的目标不远了,继续看 commands.c。

commands.c

在 commands.c 中的 dexopt() 函数,我们看到此函数从 property 中获得 dalvik.vm.dexopt-flags 和 persist.sys.dalvik.vm.lib:

/frameworks/native/cmds/installd/commands.c
C /* platform-specific flags affecting optimization and verification */ property_get(“dalvik.vm.dexopt-flags”, dexopt_flags, “”); ALOGV(“dalvik.vm.dexopt_flags=%s\n”, dexopt_flags); /* The command to run depend ones the value of persist.sys.dalvik.vm.lib */ property_get(“persist.sys.dalvik.vm.lib”, persist_sys_dalvik_vm_lib, “libdvm.so”);

1 2 3 4 5 6 /* platform-specific flags affecting optimization and verification */ property_get ( “dalvik.vm.dexopt-flags” , dexopt_flags , “” ) ; ALOGV ( “dalvik.vm.dexopt_flags=%s\n” , dexopt_flags ) ;   /* The command to run depend ones the value of persist.sys.dalvik.vm.lib */ property_get ( “persist.sys.dalvik.vm.lib” , persist_sys_dalvik_vm_lib , “libdvm.so” ) ;

dalvik.vm.dexopt-flags 暂时不知道是什么意思,persist.sys.dalvik.vm.lib 就是当前系统使用的运行时默认是 Dalvik VM,如果选择了 ART,应该就是 libart.so 了。

之后函数通过检查是否有 odex 文件来判断是否已经进行过 dexopt。然后进行一系列检查 chmod 和 chown。最后函数 fork 了一个子进程处理 dexopt:

/frameworks/native/cmds/installd/commands.c
C pid_t pid; pid = fork(); if (pid == 0) { /* child — drop privileges before continuing */ if (setgid(uid) != 0) { ALOGE(“setgid(%d) failed in installd during dexopt\n”, uid); exit(64); } if (setuid(uid) != 0) { ALOGE(“setuid(%d) failed in installd 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(out_fd, LOCK_EX | LOCK_NB) != 0) { ALOGE(“flock(%s) failed: %s\n”, out_path, strerror(errno)); exit(67); } if (strncmp(persist_sys_dalvik_vm_lib, “libdvm”, 6) == 0) { run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags); } else if (strncmp(persist_sys_dalvik_vm_lib, “libart”, 6) == 0) { run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags); } else { exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */ } exit(68); /* only get here on exec failure */ } else { res = wait_dexopt(pid, apk_path); if (res != 0) { ALOGE(“dexopt in=’%s’ out=’%s’ res=%d\n”, apk_path, out_path, res); goto fail; } }

714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 pid_t pid ; pid = fork ( ) ; if ( pid == 0 ) {      /* child — drop privileges before continuing */      if ( setgid ( uid ) != 0 ) {          ALOGE ( “setgid(%d) failed in installd during dexopt\n” , uid ) ;          exit ( 64 ) ;      }      if ( setuid ( uid ) != 0 ) {          ALOGE ( “setuid(%d) failed in installd 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 ( out_fd , LOCK_EX | LOCK_NB ) != 0 ) {          ALOGE ( “flock(%s) failed: %s\n” , out_path , strerror ( errno ) ) ;          exit ( 67 ) ;      }        if ( strncmp ( persist_sys_dalvik_vm_lib , “libdvm” , 6 ) == 0 ) {          run_dexopt ( zip_fd , out_fd , apk_path , out_path , dexopt_flags ) ;      } else if ( strncmp ( persist_sys_dalvik_vm_lib , “libart” , 6 ) == 0 ) {          run_dex2oat ( zip_fd , out_fd , apk_path , out_path , dexopt_flags ) ;      } else {          exit ( 69 ) ;    /* Unexpected persist.sys.dalvik.vm.lib value */      }      exit ( 68 ) ;    /* only get here on exec failure */ } else {      res = wait_dexopt ( pid , apk_path ) ;      if ( res != 0 ) {          ALOGE ( “dexopt in=’%s’ out=’%s’ res=%d\n” , apk_path , out_path , res ) ;          goto fail ;      } }

开始先 drop capabilities(关于 Linux capabilities: man capabilities)。最后比较 persist_sys_dalvik_vm_lib,如果是 libart 则调用 run_dex2oat()。Good,终于有头绪了,再看 run_dex2oat():

/frameworks/native/cmds/installd/commands.c
C static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, const char* output_file_name, const char* dexopt_flags) { static const char* DEX2OAT_BIN = “/system/bin/dex2oat”; static const int MAX_INT_LEN = 12; // ‘-‘+10dig+’\0’ -OR- 0x+8dig char zip_fd_arg[strlen(“–zip-fd=”) + MAX_INT_LEN]; char zip_location_arg[strlen(“–zip-location=”) + PKG_PATH_MAX]; char oat_fd_arg[strlen(“–oat-fd=”) + MAX_INT_LEN]; char oat_location_arg[strlen(“–oat-name=”) + PKG_PATH_MAX]; sprintf(zip_fd_arg, “–zip-fd=%d”, zip_fd); sprintf(zip_location_arg, “–zip-location=%s”, input_file_name); sprintf(oat_fd_arg, “–oat-fd=%d”, oat_fd); sprintf(oat_location_arg, “–oat-location=%s”, output_file_name); ALOGV(“Running %s in=%s out=%s\n”, DEX2OAT_BIN, input_file_name, output_file_name); execl(DEX2OAT_BIN, DEX2OAT_BIN, zip_fd_arg, zip_location_arg, oat_fd_arg, oat_location_arg, (char*) NULL); ALOGE(“execl(%s) failed: %s\n”, DEX2OAT_BIN, strerror(errno)); }

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 static void run_dex2oat ( int zip_fd , int oat_fd , const char * input_file_name ,      const char * output_file_name , const char * dexopt_flags ) {      static const char * DEX2OAT_BIN = “/system/bin/dex2oat” ;      static const int MAX_INT_LEN = 12 ;        // ‘-‘+10dig+’\0’ -OR- 0x+8dig      char zip_fd_arg [ strlen ( “–zip-fd=” ) + MAX_INT_LEN ] ;      char zip_location_arg [ strlen ( “–zip-location=” ) + PKG_PATH_MAX ] ;      char oat_fd_arg [ strlen ( “–oat-fd=” ) + MAX_INT_LEN ] ;      char oat_location_arg [ strlen ( “–oat-name=” ) + PKG_PATH_MAX ] ;        sprintf ( zip_fd_arg , “–zip-fd=%d” , zip_fd ) ;      sprintf ( zip_location_arg , “–zip-location=%s” , input_file_name ) ;      sprintf ( oat_fd_arg , “–oat-fd=%d” , oat_fd ) ;      sprintf ( oat_location_arg , “–oat-location=%s” , output_file_name ) ;        ALOGV ( “Running %s in=%s out=%s\n” , DEX2OAT_BIN , input_file_name , output_file_name ) ;      execl ( DEX2OAT_BIN , DEX2OAT_BIN ,            zip_fd_arg , zip_location_arg ,            oat_fd_arg , oat_location_arg ,            ( char * ) NULL ) ;      ALOGE ( “execl(%s) failed: %s\n” , DEX2OAT_BIN , strerror ( errno ) ) ; }

终于找到了,这就是调用 dex2oat 的地方,写的非常清楚。–zip-fd 是 apk 的 fd,–oat-fd 是输出 oat 的 fd。最终使用 execl() 执行 /system/bin/dex2oat 程序对 apk 进行编译。

三、总结

dex2oat 对所有 apk 进行编译并保存在 dalvik-cache 目录里。PMS 会持续扫描安装目录,如果有新的 App 安装则马上调用 dex2oat 进行编译。

最后,之前我们提到的 boot.art 我在 logcat 中找到了他的来源:


Shell /system/bin/dex2oat –image=/data/dalvik-cache/system@framework@boot.art –runtime-arg -Xms64m –runtime-arg -Xmx64m –dex-file=/system/framework/core-libart.jar –dex-file=/system/framework/conscrypt.jar –dex-file=/system/framework/okhttp.jar –dex-file=/system/framework/core-junit.jar –dex-file=/system/framework/bouncycastle.jar –dex-file=/system/framework/ext.jar –dex-file=/system/framework/framework.jar –dex-file=/system/framework/framework2.jar –dex-file=/system/framework/telephony-common.jar –dex-file=/system/framework/voip-common.jar –dex-file=/system/framework/mms-common.jar –dex-file=/system/framework/android.policy.jar –dex-file=/system/framework/services.jar –dex-file=/system/framework/apache-xml.jar –dex-file=/system/framework/webviewchromium.jar –oat-file=/data/dalvik-cache/system@framework@boot.oat –base=0x60000000 –image-classes-zip=/system/framework/framework.jar –image-classes=preloaded-classes

1 / system / bin / dex2oat image = / data / dalvik cache / system @ framework @ boot .art runtime arg Xms64m runtime arg Xmx64m dex file = / system / framework / core libart .jar dex file = / system / framework / conscrypt .jar dex file = / system / framework / okhttp .jar dex file = / system / framework / core junit .jar dex file = / system / framework / bouncycastle .jar dex file = / system / framework / ext .jar dex file = / system / framework / framework .jar dex file = / system / framework / framework2 .jar dex file = / system / framework / telephony common .jar dex file = / system / framework / voip common .jar dex file = / system / framework / mms common .jar dex file = / system / framework / android .policy .jar dex file = / system / framework / services .jar dex file = / system / framework / apache xml .jar dex file = / system / framework / webviewchromium .jar oat file = / data / dalvik cache / system @ framework @ boot .oat base = 0x60000000 image classes zip = / system / framework / framework .jar image classes = preloaded classes

似乎是把所有 framework 的 jar 编译成 boot.art,在 dex2oat 的时候使用。还是有很多不理解的地方,相信在以后的分析中应该有更深入的理解。

附:相关代码链接

This entry was posted in
Android,
Technology and tagged
Android,
ART Runtime,
dex2oat on
January 10, 2014
by Mingshen Sun.

    原文作者:cosmoslhf
    原文地址: https://blog.csdn.net/cosmoslhf/article/details/40380559
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞