ART世界探险(13) - 初入dex2oat
来源:互联网 发布:js 除去空格 换行符 编辑:程序博客网 时间:2024/04/30 01:27
ART世界探险(13) - 初入dex2oat
dex2oat流程分析
进入整个流程之前,我们先看一下地图,大致熟悉一下我们下一步要去哪里:
主函数
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的整个逻辑是很清晰的:
首先是不得不做一个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_);}
小结
最后,我们再次复习一下到目前为止学习的过程:
- ART世界探险(13) - 初入dex2oat
- ART世界探险(7) - 数组
- ART世界探险(18) InlineMethod
- ART探险(1) - oatdump看到的世界
- ART世界探险(5) - 计算指令
- ART世界探险(6) - 流程控制指令
- ART世界探险(8) - 面向对象编程
- ART世界探险(9) - 同步锁
- ART世界探险(10) - 异常处理
- ART世界探险(11) - OAT文件格式分析
- ART世界探险(2) - 从java byte code说起
- ART世界探险(14) - 快速编译器和优化编译器
- ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件
- ART世界探险(16) - 快速编译器下的方法编译
- ART世界探险(17) - 中层中间代码MIR
- ART世界探险(19) - 优化编译器的编译流程
- ART世界探险(20) - Android N上的编译流程
- Android ART Runtime – dex2oat
- android 隐藏以及显示软键盘以及不自动弹出键盘的方法
- 启动数据库报错:
- svn迁移出的项目中文乱码解决
- 进程同步的基本概念:临界资源、同步和互斥
- Installation failed since the device possibly has stale dexed jars that ...解决办法
- ART世界探险(13) - 初入dex2oat
- android 重启
- go 语言 优势及 主要用途
- 算法与追MM
- Java Swing 创建转圈的进度提示框
- 笔试题:函数中String和StringBuffer参数传递的区别
- hibernate映射不到实体的坑
- duplicate报错:ORA-12154
- Ubuntu 删除缓存(转载)