dex2oat源码流程分析

本文详细分析了Android 6.0中dex2oat的源码流程,从入口函数到 Dex2oat 对象的构造,再到 CompilerDriver 的作用,深入探讨了dex转换为oat的过程,包括预编译、类加载器处理、方法编译等关键步骤,旨在理解dex2oat在ART运行模式下的工作原理。
摘要由CSDN通过智能技术生成

dex2oat是ART运行模式下虚拟机必备的一个组件,主要用来把安装的apk和动态加载的dex等文件转换成oat文件,方便下一步的加载解析,获得其中的类并执行相关方法,所以本文以Android 6.0源码为例,对dex的处理流程尝试做一下分析,了解其中的处理情况。

dex2oat源码位于art\dex2oat\Dex2oat.cc,入口函数为main:

int main(int argc, char** argv) {
  int result = art::dex2oat(argc, argv);
  if (!art::kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
    exit(result);
  }
  return result;
}

这里的整体流程如下图所示(图片来自参考文献),后边也主要分析的这个流程上的代码:
main函数流程

直接调用了同文件art 命名空间下的 dex2oat 函数,做下一步的处理:

static int dex2oat(int argc, char** argv) {
  b13564922();

  TimingLogger timings("compiler", false, false);

  Dex2Oat dex2oat(&timings);

  // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
  dex2oat.ParseArgs(argc, argv);

  // Check early that the result of compilation can be written
  if (!dex2oat.OpenFile()) {
    return EXIT_FAILURE;
  }

  // Print the complete line when any of the following is true:
  //   1) Debug build
  //   2) Compiling an image
  //   3) Compiling with --host
  //   4) Compiling on the host (not a target build)
  // Otherwise, print a stripped command line.
  if (kIsDebugBuild || dex2oat.IsImage() || dex2oat.IsHost() || !kIsTargetBuild) {
    LOG(INFO) << CommandLine();
  } else {
    LOG(INFO) << StrippedCommandLine();
  }

  if (!dex2oat.Setup()) {
    dex2oat.EraseOatFile();
    return EXIT_FAILURE;
  }

  if (dex2oat.IsImage()) {
    return CompileImage(dex2oat);
  } else {
    return CompileApp(dex2oat);
  }
}

dex2oat的整个逻辑是很清晰的:
1. 做一个arm上的workaround,这个与我们分析的主线暂时无关,了解一下就可以了。
2. 构造Dex2oat对象
3. 处理命令行参数
4. 先行判断对于文件是否有写的权限
5. 打印命令行参数
6. 判断dex2oat的setup是否完成
7. 根据是否image分别调用CompileImage或CompileApp的处理
这里放一张来自参考文献的总体流程图:
dex2oat整体流程

CompileImage

基本上还是对于dex2oat.Compile的封装,后面都是对写文件和计时的处理:

static int CompileImage(Dex2Oat& dex2oat) {
  dex2oat.Compile();

  // Create the boot.oat.
  if (!dex2oat.CreateOatFile()) {
    dex2oat.EraseOatFile();
    return EXIT_FAILURE;
  }

  // Flush and close the boot.oat. We always expect the output file by name, and it will be
  // re-opened from the unstripped name.
  if (!dex2oat.FlushCloseOatFile()) {
    return EXIT_FAILURE;
  }

  // Creates the boot.art and patches the boot.oat.
  if (!dex2oat.HandleImage()) {
    return EXIT_FAILURE;
  }

  // When given --host, finish early without stripping.
  if (dex2oat.IsHost()) {
    dex2oat.DumpTiming();
    return EXIT_SUCCESS;
  }

  // Copy unstripped to stripped location, if necessary.
  if (!dex2oat.CopyUnstrippedToStripped()) {
    return EXIT_FAILURE;
  }

  // FlushClose again, as stripping might have re-opened the oat file.
  if (!dex2oat.FlushCloseOatFile()) {
    return EXIT_FAILURE;
  }

  dex2oat.DumpTiming();
  return EXIT_SUCCESS;
}

CompileApp

然后我们再看一下基本一模一样的CompileApp.

static int CompileApp(Dex2Oat& dex2oat) {
  dex2oat.Compile();

  // Create the app oat.
  if (!dex2oat.CreateOatFile()) {
    dex2oat.EraseOatFile();
    return EXIT_FAILURE;
  }

  // Do not close the oat file here. We might haven gotten the output file by file descriptor,
  // which we would lose.
  if (!dex2oat.FlushOatFile()) {
    return EXIT_FAILURE;
  }

  // When given --host, finish early without stripping.
  if (dex2oat.IsHost()) {
    if (!dex2oat.FlushCloseOatFile()) {
      return EXIT_FAILURE;
    }

    dex2oat.DumpTiming();
    return EXIT_SUCCESS;
  }

  // Copy unstripped to stripped location, if necessary. This will implicitly flush & close the
  // unstripped version. If this is given, we expect to be able to open writable files by name.
  if (!dex2oat.CopyUnstrippedToStripped()) {
    return EXIT_FAILURE;
  }

  // Flush and close the file.
  if (!dex2oat.FlushCloseOatFile()) {
    return EXIT_FAILURE;
  }

  dex2oat.DumpTiming();
  return EXIT_SUCCESS;
}

Compile

CompileImage和CompileApp都调用了dex2oat.Compile()函数:

  // Create and invoke the compiler driver. This will compile all the dex files.
  void Compile() {
    TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
    compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));

    // Handle and ClassLoader creation needs to come after Runtime::Create
    jobject class_loader = nullptr;
    Thread* self = Thread::Current();
    if (!boot_image_option_.empty()) {
      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
      OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
      ScopedObjectAccess soa(self);

      // Classpath: first the class-path given.
      std::vector<const DexFile*> class_path_files;
      for (auto& class_path_file : class_path_files_) {
        class_path_files.push_back(class_path_file.get());
      }

      // Store the classpath we have right now.
      key_value_store_->Put(OatHeader::kClassPathKey,
                            OatFile::EncodeDexFileDependencies(class_path_files));

      // Then the dex files we'll compile. Thus we'll resolve the class-path first.
      class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());

      class_loader = class_linker->CreatePathClassLoader(self, class_path_files);
    }

    driver_.reset(new CompilerDriver(compiler_options_.get(),
                                     verification_results_.get(),
                                     &method_inliner_map_,
                                     compiler_kind_,
                                     instruction_set_,
                                     instruction_set_features_.get(),
                                     image_,
                                     image_classes_.release(),
                                     compiled_classes_.release(),
                                     nullptr,
                                     thread_count_,
                                     dump_stats_,
                                     dump_passes_,
                                     dump_cfg_file_name_,
                                     compiler_phases_timings_.get(),
                                     swap_fd_,
                                     profile_file_));

    driver_->CompileAll(class_loader, dex_files_, timings_);
  }

Java不同于其它很多编译型语言的一点是在于它有ClassLoader。在做编译之前,先要对ClassLoader进行预处理。
然后,就创建一个CompilerDriver对象,并调用driver的ComileAll来完成编译。

CompilerDriver的构造函数

核心逻辑还是compiler_的初始化。看到构造需要这么多参数,我们需要对于dex2oat的命令行参数了解(art\compiler\driver\Compiler_driver.cc):

CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
                               VerificationResults* verification_results,
                               DexFileToMethodInlinerMap* method_inliner_map,
                               Compiler::Kind compiler_kind,
                               InstructionSet instruction_set,
                               const InstructionSetFeatures* instruction_set_features,
                               bool image, std::unordered_set<std::string>* image_classes,
                               std::
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值