美文网首页
ART世界探险(13) - 初入dex2oat

ART世界探险(13) - 初入dex2oat

作者: Jtag特工 | 来源:发表于2016-09-05 13:14 被阅读793次

    ART世界探险(13) - 初入dex2oat

    dex2oat流程分析

    进入整个流程之前,我们先看一下地图,大致熟悉一下我们下一步要去哪里:


    dex2oat main

    主函数

    dex2oat的main函数,直接是dex2oat工厂函数的封装。

    int main(int argc, char** argv) {
      int result = art::dex2oat(argc, argv);
      // Everything was done, do an explicit exit here to avoid running Runtime destructors that take
      // time (bug 10645725) unless we're a debug build or running on valgrind. Note: The Dex2Oat class
      // should not destruct the runtime in this case.
      if (!art::kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
        exit(result);
      }
      return result;
    }
    

    构造函数

    我们先看一下流程图,然后对照到代码看。

    dex2oat流程图

    dex2oat的整个逻辑是很清晰的:
    首先是不得不做一个arm上的workaround,这个与我们分析的主线暂时无关,了解一下就可以了。
    然后所做的事情:

    1. 构造Dex2oat对象
    2. 处理命令行参数
    3. 先行判断对于文件是否有写的权限
    4. 打印命令行参数
    5. 判断dex2oat的setup是否完成
    6. 根据是否image分别调用CompileImage或CompileApp的处理

    代码里面注释很详细,可读性很好,我们看一下:

    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);
      }
    }
    }  // namespace art
    

    CompileApp

    处理命令行参数等细节我们后面再补,我们先跃进到核心逻辑CompileApp中。
    我们可以看到,基本上还是对于dex2oat.Compile的封装,后面都是对写文件和计时的处理。

    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;
    }
    

    CompileImage

    然后我们再看一下完全是一个模子里面出来的CompileImage.

    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;
    }
    

    Compile

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

      // 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_ = new CompilerDriver(compiler_options_.get(),
                                     verification_results_,
                                     &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_);
      }
    

    CompilerDriver的构造函数

    核心逻辑还是compiler_的初始化。
    看到构造需要这么多参数,我们需要对于dex2oat的命令行参数进行一个复习了,我们在前面的《细说dex2oat(1)》中曾经有过对于所有参数的介绍。

    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::unordered_set<std::string>* compiled_classes,
                                   std::unordered_set<std::string>* compiled_methods,
                                   size_t thread_count, bool dump_stats, bool dump_passes,
                                   const std::string& dump_cfg_file_name, CumulativeLogger* timer,
                                   int swap_fd, const std::string& profile_file)
        : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
          swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
          profile_present_(false), compiler_options_(compiler_options),
          verification_results_(verification_results),
          method_inliner_map_(method_inliner_map),
          compiler_(Compiler::Create(this, compiler_kind)),
          compiler_kind_(compiler_kind),
          instruction_set_(instruction_set),
          instruction_set_features_(instruction_set_features),
          freezing_constructor_lock_("freezing constructor lock"),
          compiled_classes_lock_("compiled classes lock"),
          compiled_methods_lock_("compiled method lock"),
          compiled_methods_(MethodTable::key_compare()),
          non_relative_linker_patch_count_(0u),
          image_(image),
          image_classes_(image_classes),
          classes_to_compile_(compiled_classes),
          methods_to_compile_(compiled_methods),
          had_hard_verifier_failure_(false),
          thread_count_(thread_count),
          stats_(new AOTCompilationStats),
          dedupe_enabled_(true),
          dump_stats_(dump_stats),
          dump_passes_(dump_passes),
          dump_cfg_file_name_(dump_cfg_file_name),
          timings_logger_(timer),
          compiler_context_(nullptr),
          support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
          dedupe_code_("dedupe code", *swap_space_allocator_),
          dedupe_src_mapping_table_("dedupe source mapping table", *swap_space_allocator_),
          dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_),
          dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_),
          dedupe_gc_map_("dedupe gc map", *swap_space_allocator_),
          dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) {
      DCHECK(compiler_options_ != nullptr);
      DCHECK(verification_results_ != nullptr);
      DCHECK(method_inliner_map_ != nullptr);
    
      dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX);
    
      compiler_->Init();
    
      CHECK_EQ(image_, image_classes_.get() != nullptr);
    ...
    }
    

    CompilerDriver::CompileAll

    首先,CompilerDriver展现了一个值得我们学习的好习惯,为编译线程构造了一个线程池。
    在CompilerDriver进行编译的时候,分成了两个步骤:

    • PreCompile
    • Compile
    void CompilerDriver::CompileAll(jobject class_loader,
                                    const std::vector<const DexFile*>& dex_files,
                                    TimingLogger* timings) {
      DCHECK(!Runtime::Current()->IsStarted());
      std::unique_ptr<ThreadPool> thread_pool(
          new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
    ...
      PreCompile(class_loader, dex_files, thread_pool.get(), timings);
      Compile(class_loader, dex_files, thread_pool.get(), timings);
    ...
    }
    

    CompilerDriver::PreCompile

    PreCompile的步骤主要就是两个:

    • 做校验
    • 做类的初始化

    我们将前面判断是否要做校验的部分先略过,这个PreCompile的逻辑看起来就清晰得多。

    void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                                    ThreadPool* thread_pool, TimingLogger* timings) {
    ...
    
      Verify(class_loader, dex_files, thread_pool, timings);
    ...
      if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
        LOG(FATAL) << "Had a hard failure verifying all classes, and was asked to abort in such "
                   << "situations. Please check the log.";
      }
    
      InitializeClasses(class_loader, dex_files, thread_pool, timings);
    ...
    }
    

    CompilerDriver::Compile

    针对每一个dex,调用CompileDexFile去编译。

    void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                                 ThreadPool* thread_pool, TimingLogger* timings) {
      for (size_t i = 0; i != dex_files.size(); ++i) {
        const DexFile* dex_file = dex_files[i];
        CHECK(dex_file != nullptr);
        CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
      }
    ...
    }
    

    CompilerDriver::CompileDexFile

    上面的Compile函数是将多个dex拆成每一个dex文件的料度,而CompileDexFile再将其拆成每个类的粒度,针对每个类再调用CompileClass来进行编译。

    void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
                                        const std::vector<const DexFile*>& dex_files,
                                        ThreadPool* thread_pool, TimingLogger* timings) {
      TimingLogger::ScopedTiming t("Compile Dex File", timings);
      ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
                                         &dex_file, dex_files, thread_pool);
      context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
    }
    

    小结

    最后,我们再次复习一下到目前为止学习的过程:

    dex2oat-main

    相关文章

      网友评论

          本文标题:ART世界探险(13) - 初入dex2oat

          本文链接:https://www.haomeiwen.com/subject/atdrettx.html