sequenceDiagram
installd->>dex2oat: main
activate dex2oat
dex2oat->>dex2oat: art:dex2oat()
activate dex2oat
dex2oat->>dex2oat: Setup()
Note right of dex2oat: MethodVerified回调时deVirtual
deactivate dex2oat
activate dex2oat
dex2oat->>dex2oat: CompileImage()
Note right of dex2oat: 三种编译处理模式:dex2dex,jni,dex2Native
activate dex2oat
dex2oat->>dex2oat: LoadClassProfileDescriptors()
deactivate dex2oat
activate dex2oat
dex2oat->>dex2oat: Compile()
dex2oat->>CompilerDriver: CompileAll
CompilerDriver->>CompilerDriver: PreCompile
activate CompilerDriver
Note right of CompilerDriver: Resolve,Verify,InitializeClasses
deactivate CompilerDriver
activate CompilerDriver
CompilerDriver->>CompilerDriver: Compile()
activate CompilerDriver
CompilerDriver->>CompilerDriver: CompileDexFile()
Note right of CompilerDriver: 多线程编译,传递dex中的class_ids索引
activate CompilerDriver
CompilerDriver->>CompilerDriver: CompileClassVisitor
activate CompilerDriver
CompilerDriver->>CompilerDriver: CompileMethod()
Note right of CompilerDriver: AddCompiledMethod to CompilerDriver
deactivate CompilerDriver
deactivate CompilerDriver
deactivate CompilerDriver
deactivate CompilerDriver
deactivate dex2oat
deactivate dex2oat
deactivate dex2oat
dex2oat->>dex2oat: WriteOatFiles()
Note right of dex2oat: 输出.oat文件
dex2oat->>dex2oat: HandleImage()
Note right of dex2oat: 输出.art文件
graph LR
OatDexFile("OatDexFile[N]")-->DexFile("DexFile[N]")-->TypeLookup-Table("TypeLookup-Table[N]")-->ClassOffsets("ClassOffsets[N]")
ClassOffsets-->OatClass0
ClassOffsets-->OatClass1
ClassOffsets-->OatClassXxx
OatClass1-->CompliedMethod0
OatClass1-->CompliedMethod1
OatClass1-->CompliedMethodXxx
·ImageHeader中有几个成员变量关联到oat文件里的信息。其中,oat_file_begin_指向oat文件加载到内存的虚拟地址(图中是0x700ec000),oat_data_begin_指向符号oatdata的值(图中为0x700ed000),oat_data_end_指向符号oatlastword的值(图中为0x740d7e5b)。_
·art文件里的ArtMethod对象的成员变量ptr_sized_fields_结构体的entry_point_from_quick_compiled_code_指向位于oat文件里对应的code_数组。
简单来说,可以将art文件看作是很多对象通过类似序列化的方法保存到文件里而得来的。当art文件通过mmap加载到内存时,这些文件里的信息就能转换成对象直接使用。
参考《深入理解Android:Java虚拟机ART》的9.6.2.3
int main(int argc, char** argv) {
int result = art::dex2oat(argc, argv);
...
}
static int dex2oat(int argc, char** argv) {
...
dex2oat->ParseArgs(argc, argv);
if (dex2oat->UseProfileGuidedCompilation()) {
if (!dex2oat->LoadProfile()) {
return EXIT_FAILURE;
}
}
dex2oat->Setup();
bool result;
if (dex2oat->IsImage()) {
result = CompileImage(*dex2oat);
} else {
result = CompileApp(*dex2oat);
}
...
当使用profile-guide 编译app时,会先 LoadProfile(),这里就是 load /data/misc/profiles/cur/0/packagename/primary.prof,进行解析出 class index 和 method index,放到 ProfileCompilationinfo 中;
如果当前的编译要生成 image时,走CompileImage流程,否则走CompileApp流程;
bool IsImage() const {
return IsAppImage() || IsBootImage();//不论是编译boot image(boot.art)或者时 app 要生成image.
}
static int CompileApp(Dex2Oat& dex2oat) { dex2oat.Compile(); if (!dex2oat.WriteOatFiles()) { dex2oat.EraseOatFiles(); return EXIT_FAILURE; } … dex2oat.DumpTiming(); return EXIT_SUCCESS;} | static int CompileImage(Dex2Oat& dex2oat) { dex2oat.LoadClassProfileDescriptors(); dex2oat.Compile(); if (!dex2oat.WriteOatFiles()) { dex2oat.EraseOatFiles(); return EXIT_FAILURE; } … // Creates the boot.art and patches the oat files. if (!dex2oat.HandleImage()) { return EXIT_FAILURE; } … dex2oat.DumpTiming(); return EXIT_SUCCESS; |
---|---|
区别是:
编译image时需要 LoadClassProfileDescriptors() 产生 image_classes_ 集合
生成 image(HandleImage());
在生成的app image中将会包含 image_classes_ 集合中类的对象,不在 image_classes_集合中的app的类的对象,将不会被生成到 app-image中。
LoadClassProfileDescriptors()在从 profile信息中获取 image_classes_集合时,将会把 app dex 中的类以外的类,都过滤掉,比如 classpath dex 对应的类将不会生成到 app-image;
void LoadClassProfileDescriptors() {
if (profile_compilation_info_ != nullptr && app_image_) {
std::set<DexCacheResolvedClasses> resolved_classes(profile_compilation_info_->GetResolvedClasses()); // 获取 profile信息中记录的所有 class
// Filter out class path classes since we don't want to include these in the image.
std::unordered_set<std::string> dex_files_locations;
for (const DexFile* dex_file : dex_files_) {
dex_files_locations.insert(dex_file->GetLocation()); // 当前app的所有dex file
}
for (auto it = resolved_classes.begin(); it != resolved_classes.end(); ) {
if (dex_files_locations.find(it->GetDexLocation()) == dex_files_locations.end()) { // 如果这个类不在当前app 的dex file中,则过滤掉
VLOG(compiler) << "Removed profile samples for non-app dex file " << it->GetDexLocation();
it = resolved_classes.erase(it);
} else {
++it;
}
}
image_classes_.reset(new std::unordered_set<std::string>(runtime->GetClassLinker()->GetClassDescriptorsForProfileKeys(resolved_classes)));
}
// Create and invoke the compiler driver. This will compile all the dex files.
void Compile() {
...
driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
&method_inliner_map_,
compiler_kind_,
instruction_set_,
instruction_set_features_.get(),
IsBootImage(),
IsAppImage(),
image_classes_.release(),
compiled_classes_.release(),
/* compiled_methods */ nullptr,
thread_count_,
dump_stats_,
dump_passes_,
compiler_phases_timings_.get(),
swap_fd_,
profile_compilation_info_.get()));
driver_->SetDexFilesForOatFile(dex_files_);
driver_->CompileAll(class_loader_, dex_files_, timings_);
编译dex文件时在 CompilerDriver 中完成, 其中LoadProfile时构造的 ==profile_compilation_info_也会指导 将要编译哪些class和 methods==。
driver_->SetDexFilesForOatFile(dex_files_);//表示将要编译的所有 dex file,这个集合是 –dex-file=/data/app/com.facebook.katana-1/base.apk 这个文件中包含的所有dex文件,比如facebook的apk中有 12个 dex文件,则会依次编译这12个文件。
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
InitializeThreadPools();
// Precompile:
// 1) Load image classes
// 2) Resolve all classes
// 3) Attempt to verify all classes
// 4) Attempt to initialize image classes, and trivially initialized classes
PreCompile(class_loader, dex_files, timings);
// Compile:
// 1) Compile all classes and methods enabled for compilation.
if (!GetCompilerOptions().VerifyAtRuntime()) {
Compile(class_loader, dex_files, timings);
}
}
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
LoadImageClasses(timings); //这里只针对 bootimage的编译
Resolve(class_loader, dex_files, timings);
Verify(class_loader, dex_files, timings);
InitializeClasses(class_loader, dex_files, timings);
}
void CompilerDriver::Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
VerifyDexFile(class_loader,
*dex_file,
dex_files,
parallel_thread_pool_.get(),
parallel_thread_count_,
timings);
}
}
void CompilerDriver::VerifyDexFile(...){
...
VerifyClassVisitor visitor(&context, log_level);
context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
class VerifyClassVisitor : public CompilationVisitor {
public:
VerifyClassVisitor(const ParallelCompilationManager* manager, LogSeverity log_level)
: manager_(manager), log_level_(log_level) {}
virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
if (!manager_->GetCompiler()->ShouldVerifyClassBasedOnProfile(dex_file, class_def_index)) {
// Skip verification since the class is not in the profile.
return;
}
...
}
}
bool CompilerDriver::ShouldVerifyClassBasedOnProfile(const DexFile& dex_file,
uint16_t class_idx) const {
...
bool result = profile_compilation_info_->ContainsClass(dex_file, class_idx);
return result;
在这里可以看到,==前面从 profile中load出来的信息,将会决定只有这些 class才会进行Verify==。
接下来看下真正的编译,实际上编译对应的是 dalvik bytecode到 native code的转换,主要针对的 method;
void CompilerDriver::Compile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
CompileDexFile(class_loader, *dex_file, dex_files, parallel_thread_pool_.get(), parallel_thread_count_, timings); // 按照dexfile 依次编译
}
...
}
void CompilerDriver::CompileDexFile(jobject class_loader,
const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool,
size_t thread_count,
TimingLogger* timings) {
TimingLogger::ScopedTiming t("Compile Dex File", timings);
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
&dex_file, dex_files, thread_pool);
CompileClassVisitor visitor(&context);
context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count); //从dexfile的第一个class,直到最后一个class
编译的工作在 CompileClassVisitor 的Visit方法中进行;
class CompileClassVisitor : public CompilationVisitor {
public:
explicit CompileClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE { // 传递的参数为 class在 dexfile中的 index,以此来查找class 数据
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
const char* descriptor = dex_file.GetClassDescriptor(class_def);
Handle<mirror::Class> klass(hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
const uint8_t* class_data = dex_file.GetClassData(class_def);
ClassDataItemIterator it(dex_file, class_data);
while (it.HasNextDirectMethod()) { // 编译direct mothod
uint32_t method_idx = it.GetMemberIndex();
CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
compilation_enabled, dex_cache);
it.Next();
}
while (it.HasNextVirtualMethod()) { // 编译virtual methods
uint32_t method_idx = it.GetMemberIndex();
CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
compilation_enabled, dex_cache);
it.Next();
}
...
}
从这一步中,我们可以看到,编译代码工作,主要的就是编译 method成为 native code;
static void CompileMethod(Thread* self,
CompilerDriver* driver,
const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
jobject class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level,
bool compilation_enabled,
Handle<mirror::DexCache> dex_cache)
REQUIRES(!driver->compiled_methods_lock_) {
MethodReference method_ref(&dex_file, method_idx);
if ((access_flags & kAccNative) != 0) { // 编译 JNI 函数
compiled_method = driver->GetCompiler()->JniCompile(access_flags, method_idx, dex_file);
} else if((access_flags & kAccAbstract) != 0) { // abstract 函数没有代码,不需要编译
} else { //编译其他函数
const VerifiedMethod* verified_method =
driver->GetVerificationResults()->GetVerifiedMethod(method_ref);
bool compile = compilation_enabled &&
driver->GetVerificationResults()
->IsCandidateForCompilation(method_ref, access_flags) &&
verified_method != nullptr &&
!verified_method->HasRuntimeThrow() &&
(verified_method->GetEncounteredVerificationFailures() &
(verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 &&
driver->IsMethodToCompile(method_ref) &&
driver->ShouldCompileBasedOnProfile(method_ref);// 如果是profile-guide编译,需要检查是否是 profile中指定的函数,如果不是,则不编译该函数
if (compile) {
// NOTE: if compiler declines to compile this method, it will return null.
compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
class_def_idx, method_idx, class_loader,
dex_file, dex_cache);
}
...
driver->AddCompiledMethod(method_ref, compiled_method, non_relative_linker_patch_count);//把编译得到的 compiled-method 添加到 compiler-driver中,以便后面生成oat文件时使用
}
bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const {
if (profile_compilation_info_ == nullptr) {
// If we miss profile information it means that we don't do a profile guided compilation.
// Return true, and let the other filters decide if the method should be compiled.
return true;
}
bool result = profile_compilation_info_->ContainsMethod(method_ref);// 判断当前method是不是在前面 load到的 profile 中
return result;
compiled-method 的生成过程,是真正ART编译器工作的过程,使用了图等算法进行编译,非常复杂,这里不再详述,总之,这个过程中,完成了dalvik bytecode 到 native code的转化以及一定的优化,到这一步,我们得到了产出: compiled-method,ART运行过程中,执行函数时,如果这个函数被编译过,那么就会执行其对应的 compiled-method,否则继续解释执行其对应的 dalvik bytecode。
在Compile流程结束后,会进行OAT文件的写入操作。
enum class WriteState {
kAddingDexFileSources, // 添加dex文件到 oat文件中
kPrepareLayout, //准备文件布局
kWriteRoData, //写入RoData
kWriteText, //写入代码段
kWriteHeader, // 写入 oat header
kDone // 写入完成
}
从OatWriteState可以看到,其写入oat文件的流程。
bool Setup() {
CreateOatWriters();
if (!AddDexFileSources()) {
return false;
}
}
bool WriteOatFiles() {
if (IsImage()) { // 如果本次dex2oat要生成 image,则会在写入 oat文件时,做准备工作
image_writer_.reset(new ImageWriter(*driver_, image_base_, compiler_options_->GetCompilePic(),IsAppImage(), image_storage_mode_, oat_filenames_, dex_file_oat_index_map_));
if (!image_writer_->PrepareImageAddressSpace()) {
LOG(ERROR) << "Failed to prepare image address space.";
return false;
}
}
oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files, &patcher);
size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
size_t text_size = oat_writer->GetSize() - rodata_size;
elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize());
if (!oat_writer->WriteRodata(rodata)) {
LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file->GetPath();
return false;
}
OutputStream* text = elf_writer->StartText();
if (!oat_writer->WriteCode(text)) {
LOG(ERROR) << "Failed to write .text section to the ELF file " << oat_file->GetPath();
return false;
}
if (!oat_writer->WriteHeader(elf_writer->GetStream(),
image_file_location_oat_checksum_,
image_file_location_oat_data_begin_,
image_patch_delta_)) {
LOG(ERROR) << "Failed to write oat header to the ELF file " << oat_file->GetPath();
return false;
}
OAT文件的写入流程就是按照这几个步骤完成,可以参照oat文件的加载完成OAT文件格式的详细了解。
verification_results_.reset(new VerificationResults(compiler_options_.get()));
在做类校验时,外界可以传递一个回调接口对象。
·当类校验失败时,该接口对象的ClassRejected函数将被调用。
·当类的Java方法校验通过时,该接口对象的MethodVerified函数将被调用。
callbacks_.reset(new QuickCompilerCallbacks( verification_results_.get(),&method_inliner_map_,
IsBootImage() ? CompilerCallbacks::CallbackMode::kCompileBootImage : CompilerCallbacks::CallbackMode::kCompileApp));
const VerifiedMethod* VerifiedMethod::Create(
verifier::MethodVerifier* method_verifier, bool compile) {
if (compile) {//compile为true时表示这个方法将会被编译。
//如果这个Java方法中有invoke-virtual或invoke-interface相关的指令,则下面if的条
//件满足
if (method_verifier->HasVirtualOrInterfaceInvokes()) {
//去虚拟化de virtual。下面将介绍这个函数
verified_method->GenerateDevirtMap(method_verifier);
}
在dex2oat中,一个Java方法根据其具体情况有三种编译处理模式
1: dex到dex的编译
2: jni方法的编译
3: dex字节码到机器码的编译
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,....) {
......
if ((never_verify || verification_enabled) && !verify_only_profile) {
/*下面的Resolve函数主要工作为遍历dex文件,然后:
(1)解析其中的类型,即遍历dex文件里的type_ids数组。内部将调用ClassLinker的ResolveType函数。
(2)解析dex里的类、成员变量、成员函数。内部将调用ClassLinker的ResolveType、ResolveField和ResolveMethod等函数。读者可回顾8.7.8.1节的内容。 */
Resolve(class_loader, dex_files, timings);
}
/*下面两个函数的作用:
(1)Verify:遍历dex文件,校验其中的类。校验结果通过QuickCompilationCallback存储在
CompilerDriver的verification_results_中。
(2)InitializeClasses:遍历dex文件,确保类的初始化。*/
Verify(class_loader, dex_files, timings);
InitializeClasses(class_loader, dex_files, timings);
}
void CompilerDriver::Compile(jobject class_loader,
const std::vector<const DexFile*>& dex_files, TimingLogger* timings) {
for (const DexFile* dex_file : dex_files) {
CompileDexFile(class_loader,*dex_file,dex_files,......);
......
}
void CompilerDriver::CompileDexFile(jobject class_loader,
const DexFile& dex_file, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, size_t thread_count, TimingLogger* timings) {
CompileClassVisitor visitor(&context);
/*context.ForAll将触发线程池进行编译工作。注意,编译是以类为单位进行处理的,每一个待编译
的类都会交由CompileClassVisitor的Visit函数进行处理。*/
context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
//编译时,编译线程将调用下面的这个Visit函数,参数为待处理类在dex文件里class_ids数组中的索引
virtual void Visit(size_t class_def_index) ..... {
//遍历direct的Java方法
int64_t previous_direct_method_idx = -1;
while (it.HasNextDirectMethod()) {
uint32_t method_idx = it.GetMemberIndex();
.....
previous_direct_method_idx = method_idx;
CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(),
it.GetMethodAccessFlags(),it.GetMethodInvokeType(class_def),
class_def_index, method_idx, jclass_loader, dex_file,
dex_to_dex_compilation_level,compilation_enabled,
dex_cache);
it.Next();
}
//编译虚函数,也是调用CompileMethod函数
}