diff options
45 files changed, 1189 insertions, 428 deletions
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 713f8eb05d..893cad288b 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -49,7 +49,7 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) { std::vector<const char*> names; - CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item); + CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item, mi->dex_method_index); if (accessor.HasCodeItem()) { DCHECK(mi->dex_file != nullptr); const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset()); @@ -163,7 +163,7 @@ class ElfCompilationUnitWriter { for (auto mi : compilation_unit.methods) { DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - CodeItemDebugInfoAccessor accessor(*dex, mi->code_item); + CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index); const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index); const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method); const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 4e37f4e4ba..44504c1efb 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -159,7 +159,7 @@ class ElfDebugLineWriter { PositionInfos dex2line_map; DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - CodeItemDebugInfoAccessor accessor(*dex, mi->code_item); + CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index); const uint32_t debug_info_offset = accessor.DebugInfoOffset(); if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) { continue; diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 52cb217980..308e75d9c1 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -373,15 +373,15 @@ CompiledMethod* ArtCompileDEX( CHECK_EQ(quicken_count, dex_compiler.GetQuickenedInfo().size()); } std::vector<uint8_t> quicken_data; + QuickenInfoTable::Builder builder(&quicken_data, dex_compiler.GetQuickenedInfo().size()); + // Length is encoded by the constructor. for (QuickenedInfo info : dex_compiler.GetQuickenedInfo()) { // Dex pc is not serialized, only used for checking the instructions. Since we access the // array based on the index of the quickened instruction, the indexes must line up perfectly. // The reader side uses the NeedsIndexForInstruction function too. const Instruction& inst = unit.GetCodeItemAccessor().InstructionAt(info.dex_pc); CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode(); - // Add the index. - quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0)); - quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8)); + builder.AddIndex(info.dex_member_index); } InstructionSet instruction_set = driver->GetInstructionSet(); if (instruction_set == InstructionSet::kThumb2) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c0886d0185..869865956c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -424,10 +424,6 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( // optimizations that could break that. max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; } - if (!VdexFile::CanEncodeQuickenedData(dex_file)) { - // Don't do any dex level optimizations if we cannot encode the quickening. - return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; - } if (klass->IsVerified()) { // Class is verified so we can enable DEX-to-DEX compilation for performance. return max_level; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index af537dd653..a1a5692ef6 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -43,7 +43,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, CompilerDriver* driver, CodeGenerator* code_generator, OptimizingCompilerStats* compiler_stats, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), @@ -70,7 +70,6 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, compiler_driver_(nullptr), code_generator_(nullptr), compilation_stats_(nullptr), - interpreter_metadata_(nullptr), handles_(handles), return_type_(return_type) {} diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index c16a3a928d..5a1914ce08 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "base/arena_object.h" +#include "base/array_ref.h" #include "dex/code_item_accessors.h" #include "dex/dex_file-inl.h" #include "dex/dex_file.h" @@ -40,7 +41,7 @@ class HGraphBuilder : public ValueObject { CompilerDriver* driver, CodeGenerator* code_generator, OptimizingCompilerStats* compiler_stats, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, VariableSizedHandleScope* handles); // Only for unit testing. @@ -73,7 +74,7 @@ class HGraphBuilder : public ValueObject { CodeGenerator* const code_generator_; OptimizingCompilerStats* const compilation_stats_; - const uint8_t* const interpreter_metadata_; + const ArrayRef<const uint8_t> interpreter_metadata_; VariableSizedHandleScope* const handles_; const DataType::Type return_type_; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index b2ad8ec400..81a75584a4 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1660,7 +1660,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); uint32_t method_index = resolved_method->GetDexMethodIndex(); - CodeItemDebugInfoAccessor code_item_accessor(callee_dex_file, code_item); + CodeItemDebugInfoAccessor code_item_accessor(resolved_method); ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(), caller_compilation_unit_.GetDexCache(), diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 72a93c1f77..64a1eccf60 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -49,7 +49,7 @@ HInstructionBuilder::HInstructionBuilder(HGraph* graph, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* compiler_driver, CodeGenerator* code_generator, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, ScopedArenaAllocator* local_allocator) diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 708a09711a..4428c53277 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_ #define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_ +#include "base/array_ref.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "data_type.h" @@ -57,7 +58,7 @@ class HInstructionBuilder : public ValueObject { const DexCompilationUnit* outer_compilation_unit, CompilerDriver* compiler_driver, CodeGenerator* code_generator, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, ScopedArenaAllocator* local_allocator); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f4115f7e7b..a3b1f0c5af 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -772,7 +772,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, return nullptr; } - CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item); + CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item, method_idx); HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -783,7 +783,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, compiler_driver->GetCompilerOptions().GetDebuggable(), osr); - const uint8_t* interpreter_metadata = nullptr; + ArrayRef<const uint8_t> interpreter_metadata; // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); @@ -940,7 +940,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( compiler_driver, codegen.get(), compilation_stats_.get(), - /* interpreter_metadata */ nullptr, + /* interpreter_metadata */ ArrayRef<const uint8_t>(), handles); builder.BuildIntrinsicGraph(method); } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 23af2ab6e3..83a86567a3 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1807,9 +1807,7 @@ class Dex2Oat FINAL { // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening // optimization does not depend on the boot image (the optimization relies on not // having final fields in a class, which does not change for an app). - VdexFile::Unquicken(dex_files_, - input_vdex_file_->GetQuickeningInfo(), - /* decompile_return_instruction */ false); + input_vdex_file_->Unquicken(dex_files_, /* decompile_return_instruction */ false); } else { // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate // the results for all the dex files, not just the results for the current dex file. diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index cecd376be5..a81fa76e80 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -58,6 +58,7 @@ #include "mirror/object-inl.h" #include "oat_quick_method_header.h" #include "os.h" +#include "quicken_info.h" #include "safe_map.h" #include "scoped_thread_state_change-inl.h" #include "type_lookup_table.h" @@ -2618,42 +2619,54 @@ bool OatWriter::WriteRodata(OutputStream* out) { return true; } -class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { +class OatWriter::WriteQuickeningInfoMethodVisitor { public: - WriteQuickeningInfoMethodVisitor(OatWriter* writer, - OutputStream* out, - uint32_t offset, - SafeMap<const uint8_t*, uint32_t>* offset_map) - : DexMethodVisitor(writer, offset), - out_(out), - written_bytes_(0u), - offset_map_(offset_map) {} + WriteQuickeningInfoMethodVisitor(OatWriter* writer, OutputStream* out) + : writer_(writer), + out_(out) {} - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) - OVERRIDE { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); + bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { + std::vector<uint8_t> empty_quicken_info; + { + // Since we need to be able to access by dex method index, put a one byte empty quicken info + // for any method that isn't quickened. + QuickenInfoTable::Builder empty_info(&empty_quicken_info, /*num_elements*/ 0u); + CHECK(!empty_quicken_info.empty()); + } + for (const DexFile* dex_file : dex_files) { + std::vector<uint32_t>* const offsets = + &quicken_info_offset_indices_.Put(dex_file, std::vector<uint32_t>())->second; + + // Every method needs an index in the table. + for (uint32_t method_idx = 0; method_idx < dex_file->NumMethodIds(); ++method_idx) { + ArrayRef<const uint8_t> map(empty_quicken_info); + + // Use the existing quicken info if it exists. + MethodReference method_ref(dex_file, method_idx); + CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(method_ref); + if (compiled_method != nullptr && HasQuickeningInfo(compiled_method)) { + map = compiled_method->GetVmapTable(); + } - if (HasQuickeningInfo(compiled_method)) { - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - // Deduplication is already done on a pointer basis by the compiler driver, - // so we can simply compare the pointers to find out if things are duplicated. - if (offset_map_->find(map.data()) == offset_map_->end()) { - uint32_t length = map.size() * sizeof(map.front()); - offset_map_->Put(map.data(), written_bytes_); - if (!out_->WriteFully(&length, sizeof(length)) || - !out_->WriteFully(map.data(), length)) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " - << out_->GetLocation(); + // The current approach prevents deduplication of quicken infos since each method index + // has one unique quicken info. Deduplication does not provide much savings for dex indices + // since they are rarely duplicated. + const uint32_t length = map.size() * sizeof(map.front()); + + // Record each index if required. written_bytes_ is the offset from the start of the + // quicken info data. + if (QuickenInfoOffsetTableAccessor::IsCoveredIndex(method_idx)) { + offsets->push_back(written_bytes_); + } + + if (!out_->WriteFully(map.data(), length)) { + PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod() + << " to " << out_->GetLocation(); return false; } - written_bytes_ += sizeof(length) + length; - offset_ += sizeof(length) + length; + written_bytes_ += length; } } - return true; } @@ -2661,71 +2674,59 @@ class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { return written_bytes_; } + SafeMap<const DexFile*, std::vector<uint32_t>>& GetQuickenInfoOffsetIndicies() { + return quicken_info_offset_indices_; + } + + private: + OatWriter* const writer_; OutputStream* const out_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - SafeMap<const uint8_t*, uint32_t>* offset_map_; + size_t written_bytes_ = 0u; + // Map of offsets for quicken info related to method indices. + SafeMap<const DexFile*, std::vector<uint32_t>> quicken_info_offset_indices_; }; -class OatWriter::WriteQuickeningIndicesMethodVisitor { +class OatWriter::WriteQuickeningInfoOffsetsMethodVisitor { public: - WriteQuickeningIndicesMethodVisitor(OutputStream* out, - uint32_t quickening_info_bytes, - const SafeMap<const uint8_t*, uint32_t>& offset_map) + WriteQuickeningInfoOffsetsMethodVisitor( + OutputStream* out, + uint32_t start_offset, + SafeMap<const DexFile*, std::vector<uint32_t>>* quicken_info_offset_indices, + std::vector<uint32_t>* out_table_offsets) : out_(out), - quickening_info_bytes_(quickening_info_bytes), - written_bytes_(0u), - offset_map_(offset_map) {} + start_offset_(start_offset), + quicken_info_offset_indices_(quicken_info_offset_indices), + out_table_offsets_(out_table_offsets) {} - bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) { + bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { for (const DexFile* dex_file : dex_files) { - const size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - for (ClassDataItemIterator class_it(*dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (!class_it.IsAtMethod() || class_it.GetMethodCodeItem() == nullptr) { - continue; - } - uint32_t method_idx = class_it.GetMemberIndex(); - CompiledMethod* compiled_method = - driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); - const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - CodeItemDebugInfoAccessor accessor(*dex_file, code_item); - const uint32_t existing_debug_info_offset = accessor.DebugInfoOffset(); - // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) - // we will pretend the method has been quickened. - bool existing_offset_out_of_bounds = - (existing_debug_info_offset >= dex_file->Size() && - existing_debug_info_offset != 0xFFFFFFFF); - bool has_quickening_info = HasQuickeningInfo(compiled_method); - if (has_quickening_info || existing_offset_out_of_bounds) { - uint32_t new_debug_info_offset = - dex_file->Size() + quickening_info_bytes_ + written_bytes_; - // Abort if overflow. - CHECK_GE(new_debug_info_offset, dex_file->Size()); - const_cast<DexFile::CodeItem*>(code_item)->SetDebugInfoOffset(new_debug_info_offset); - uint32_t quickening_offset = has_quickening_info - ? offset_map_.Get(compiled_method->GetVmapTable().data()) - : VdexFile::kNoQuickeningInfoOffset; - if (!out_->WriteFully(&existing_debug_info_offset, - sizeof(existing_debug_info_offset)) || - !out_->WriteFully(&quickening_offset, sizeof(quickening_offset))) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file->PrettyMethod(method_idx) << " to " - << out_->GetLocation(); - return false; - } - written_bytes_ += sizeof(existing_debug_info_offset) + sizeof(quickening_offset); - } - } + auto it = quicken_info_offset_indices_->find(dex_file); + DCHECK(it != quicken_info_offset_indices_->end()) << "Failed to find dex file " + << dex_file->GetLocation(); + const std::vector<uint32_t>* const offsets = &it->second; + + const uint32_t current_offset = start_offset_ + written_bytes_; + CHECK_ALIGNED_PARAM(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + + // Generate and write the data. + std::vector<uint8_t> table_data; + QuickenInfoOffsetTableAccessor::Builder builder(&table_data); + for (uint32_t offset : *offsets) { + builder.AddOffset(offset); + } + + // Store the offset since we need to put those after the dex file. Table offsets are relative + // to the start of the quicken info section. + out_table_offsets_->push_back(current_offset); + + const uint32_t length = table_data.size() * sizeof(table_data.front()); + if (!out_->WriteFully(table_data.data(), length)) { + PLOG(ERROR) << "Failed to write quickening offset table for " << dex_file->GetLocation() + << " to " << out_->GetLocation(); + return false; } + written_bytes_ += length; } return true; } @@ -2736,14 +2737,16 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { private: OutputStream* const out_; - const uint32_t quickening_info_bytes_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - const SafeMap<const uint8_t*, uint32_t>& offset_map_; + const uint32_t start_offset_; + size_t written_bytes_ = 0u; + // Maps containing the offsets for the tables. + SafeMap<const DexFile*, std::vector<uint32_t>>* const quicken_info_offset_indices_; + std::vector<uint32_t>* const out_table_offsets_; }; bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { size_t initial_offset = vdex_size_; + // Make sure the table is properly aligned. size_t start_offset = RoundUp(initial_offset, 4u); off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); @@ -2754,36 +2757,71 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { return false; } - if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { + size_t current_offset = start_offset; + if (compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { std::vector<uint32_t> dex_files_indices; - SafeMap<const uint8_t*, uint32_t> offset_map; - WriteQuickeningInfoMethodVisitor visitor1(this, vdex_out, start_offset, &offset_map); - if (!VisitDexMethods(&visitor1)) { + WriteQuickeningInfoMethodVisitor write_quicken_info_visitor(this, vdex_out); + if (!write_quicken_info_visitor.VisitDexMethods(*dex_files_)) { PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); return false; } - if (visitor1.GetNumberOfWrittenBytes() > 0) { - WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, - visitor1.GetNumberOfWrittenBytes(), - offset_map); - if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " - << vdex_out->GetLocation(); + uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes(); + current_offset = current_offset + quicken_info_offset; + uint32_t before_offset = current_offset; + current_offset = RoundUp(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + const size_t extra_bytes = current_offset - before_offset; + quicken_info_offset += extra_bytes; + actual_offset = vdex_out->Seek(current_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(current_offset)) { + PLOG(ERROR) << "Failed to seek to quickening offset table section. Actual: " << actual_offset + << " Expected: " << current_offset + << " Output: " << vdex_out->GetLocation(); + return false; + } + + std::vector<uint32_t> table_offsets; + WriteQuickeningInfoOffsetsMethodVisitor table_visitor( + vdex_out, + quicken_info_offset, + &write_quicken_info_visitor.GetQuickenInfoOffsetIndicies(), + /*out*/ &table_offsets); + if (!table_visitor.VisitDexMethods(*dex_files_)) { + PLOG(ERROR) << "Failed to write the vdex quickening info. File: " + << vdex_out->GetLocation(); + return false; + } + + CHECK_EQ(table_offsets.size(), dex_files_->size()); + + current_offset += table_visitor.GetNumberOfWrittenBytes(); + + // Store the offset table offset as a preheader for each dex. + size_t index = 0; + for (const OatDexFile& oat_dex_file : oat_dex_files_) { + const off_t desired_offset = oat_dex_file.dex_file_offset_ - + sizeof(VdexFile::QuickeningTableOffsetType); + actual_offset = vdex_out->Seek(desired_offset, kSeekSet); + if (actual_offset != desired_offset) { + PLOG(ERROR) << "Failed to seek to before dex file for writing offset table offset: " + << actual_offset << " Expected: " << desired_offset + << " Output: " << vdex_out->GetLocation(); return false; } - - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." + uint32_t offset = table_offsets[index]; + if (!vdex_out->WriteFully(reinterpret_cast<const uint8_t*>(&offset), sizeof(offset))) { + PLOG(ERROR) << "Failed to write verifier deps." << " File: " << vdex_out->GetLocation(); return false; } - size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + - visitor2.GetNumberOfWrittenBytes(); - } else { - // We know we did not quicken. - size_quickening_info_ = 0; + ++index; } + if (!vdex_out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing quickening info." + << " File: " << vdex_out->GetLocation(); + return false; + } + size_quickening_info_ = current_offset - start_offset; } else { // We know we did not quicken. size_quickening_info_ = 0; @@ -3358,9 +3396,15 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex // Dex files are required to be 4 byte aligned. size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4); - size_t file_offset = start_offset; size_dex_file_alignment_ += start_offset - initial_offset; + // Leave extra room for the quicken offset table offset. + start_offset += sizeof(VdexFile::QuickeningTableOffsetType); + // TODO: Not count the offset as part of alignment. + size_dex_file_alignment_ += sizeof(VdexFile::QuickeningTableOffsetType); + + size_t file_offset = start_offset; + // Seek to the start of the dex file and flush any pending operations in the stream. // Verify that, after flushing the stream, the file is at the same offset as the stream. off_t actual_offset = out->Seek(file_offset, kSeekSet); diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index c9deea9a4b..824b395b02 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -275,7 +275,7 @@ class OatWriter { class WriteMapMethodVisitor; class WriteMethodInfoVisitor; class WriteQuickeningInfoMethodVisitor; - class WriteQuickeningIndicesMethodVisitor; + class WriteQuickeningInfoOffsetsMethodVisitor; // Visit all the methods in all the compiled dex files in their definition order // with a given DexMethodVisitor. diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 99bc1adabb..321a2e4d46 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -659,7 +659,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader(); - ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { + // If quickening is enabled we will always write the table since there is no special logic that + // checks for all methods not being quickened (not worth the complexity). + ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + } int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 8132323203..1518e1d205 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1187,7 +1187,7 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, */ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, const DexFile::CodeItem* pCode, u4 codeOffset) { - CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx); fprintf(gOutFile, " registers : %d\n", accessor.RegistersSize()); fprintf(gOutFile, " ins : %d\n", accessor.InsSize()); diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index 1c5b16d84b..d1dc6587c0 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -16,10 +16,144 @@ #include "compact_dex_writer.h" +#include "base/logging.h" +#include "base/time_utils.h" +#include "dex/compact_dex_debug_info.h" #include "dex/compact_dex_file.h" +#include "dexlayout.h" namespace art { +uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(uint32_t offset) { + const uint32_t start_offset = offset; + const dex_ir::Collections& collections = header_->GetCollections(); + // Debug offsets for method indexes. 0 means no debug info. + std::vector<uint32_t> debug_info_offsets(collections.MethodIdsSize(), 0u); + + static constexpr InvokeType invoke_types[] = { + kDirect, + kVirtual + }; + + for (InvokeType invoke_type : invoke_types) { + for (const std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) { + // Skip classes that are not defined in this dex file. + dex_ir::ClassData* class_data = class_def->GetClassData(); + if (class_data == nullptr) { + continue; + } + for (auto& method : *(invoke_type == InvokeType::kDirect + ? class_data->DirectMethods() + : class_data->VirtualMethods())) { + const dex_ir::MethodId* method_id = method->GetMethodId(); + dex_ir::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr && code_item->DebugInfo() != nullptr) { + const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset(); + const uint32_t method_idx = method_id->GetIndex(); + if (debug_info_offsets[method_idx] != 0u) { + CHECK_EQ(debug_info_offset, debug_info_offsets[method_idx]); + } + debug_info_offsets[method_idx] = debug_info_offset; + } + } + } + } + + std::vector<uint8_t> data; + debug_info_base_ = 0u; + debug_info_offsets_table_offset_ = 0u; + CompactDexDebugInfoOffsetTable::Build(debug_info_offsets, + &data, + &debug_info_base_, + &debug_info_offsets_table_offset_); + // Align the table and write it out. + offset = RoundUp(offset, CompactDexDebugInfoOffsetTable::kAlignment); + debug_info_offsets_pos_ = offset; + offset += Write(data.data(), data.size(), offset); + + // Verify that the whole table decodes as expected and measure average performance. + const bool kMeasureAndTestOutput = dex_layout_->GetOptions().verify_output_; + if (kMeasureAndTestOutput && !debug_info_offsets.empty()) { + uint64_t start_time = NanoTime(); + CompactDexDebugInfoOffsetTable::Accessor accessor(mem_map_->Begin() + debug_info_offsets_pos_, + debug_info_base_, + debug_info_offsets_table_offset_); + + for (size_t i = 0; i < debug_info_offsets.size(); ++i) { + CHECK_EQ(accessor.GetDebugInfoOffset(i), debug_info_offsets[i]); + } + uint64_t end_time = NanoTime(); + VLOG(dex) << "Average lookup time (ns) for debug info offsets: " + << (end_time - start_time) / debug_info_offsets.size(); + } + + return offset - start_offset; +} + +uint32_t CompactDexWriter::WriteCodeItem(dex_ir::CodeItem* code_item, + uint32_t offset, + bool reserve_only) { + DCHECK(code_item != nullptr); + const uint32_t start_offset = offset; + offset = RoundUp(offset, CompactDexFile::CodeItem::kAlignment); + ProcessOffset(&offset, code_item); + + CompactDexFile::CodeItem disk_code_item; + disk_code_item.registers_size_ = code_item->RegistersSize(); + disk_code_item.ins_size_ = code_item->InsSize(); + disk_code_item.outs_size_ = code_item->OutsSize(); + disk_code_item.tries_size_ = code_item->TriesSize(); + disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize(); + // Avoid using sizeof so that we don't write the fake instruction array at the end of the code + // item. + offset += Write(&disk_code_item, + OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_), + offset); + // Write the instructions. + offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); + // Write the post instruction data. + offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only); + return offset - start_offset; +} + +void CompactDexWriter::SortDebugInfosByMethodIndex() { + dex_ir::Collections& collections = header_->GetCollections(); + static constexpr InvokeType invoke_types[] = { + kDirect, + kVirtual + }; + std::map<const dex_ir::DebugInfoItem*, uint32_t> method_idx_map; + for (InvokeType invoke_type : invoke_types) { + for (std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) { + // Skip classes that are not defined in this dex file. + dex_ir::ClassData* class_data = class_def->GetClassData(); + if (class_data == nullptr) { + continue; + } + for (auto& method : *(invoke_type == InvokeType::kDirect + ? class_data->DirectMethods() + : class_data->VirtualMethods())) { + const dex_ir::MethodId* method_id = method->GetMethodId(); + dex_ir::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr && code_item->DebugInfo() != nullptr) { + const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo(); + method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex())); + } + } + } + } + std::sort(collections.DebugInfoItems().begin(), + collections.DebugInfoItems().end(), + [&](const std::unique_ptr<dex_ir::DebugInfoItem>& a, + const std::unique_ptr<dex_ir::DebugInfoItem>& b) { + auto it_a = method_idx_map.find(a.get()); + auto it_b = method_idx_map.find(b.get()); + uint32_t idx_a = it_a != method_idx_map.end() ? it_a->second : 0u; + uint32_t idx_b = it_b != method_idx_map.end() ? it_b->second : 0u; + return idx_a < idx_b; + }); +} + void CompactDexWriter::WriteHeader() { CompactDexFile::Header header; CompactDexFile::WriteMagic(&header.magic_[0]); @@ -49,6 +183,11 @@ void CompactDexWriter::WriteHeader() { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); + + // Compact dex specific flags. + header.debug_info_offsets_pos_ = debug_info_offsets_pos_; + header.debug_info_offsets_table_offset_ = debug_info_offsets_table_offset_; + header.debug_info_base_ = debug_info_base_; header.feature_flags_ = 0u; // In cases where apps are converted to cdex during install, maintain feature flags so that // the verifier correctly verifies apps that aren't targetting default methods. @@ -62,4 +201,103 @@ size_t CompactDexWriter::GetHeaderSize() const { return sizeof(CompactDexFile::Header); } +void CompactDexWriter::WriteMemMap() { + // Starting offset is right after the header. + uint32_t offset = GetHeaderSize(); + + dex_ir::Collections& collection = header_->GetCollections(); + + // Based on: https://source.android.com/devices/tech/dalvik/dex-format + // Since the offsets may not be calculated already, the writing must be done in the correct order. + const uint32_t string_ids_offset = offset; + offset += WriteStringIds(offset, /*reserve_only*/ true); + offset += WriteTypeIds(offset); + const uint32_t proto_ids_offset = offset; + offset += WriteProtoIds(offset, /*reserve_only*/ true); + offset += WriteFieldIds(offset); + offset += WriteMethodIds(offset); + const uint32_t class_defs_offset = offset; + offset += WriteClassDefs(offset, /*reserve_only*/ true); + const uint32_t call_site_ids_offset = offset; + offset += WriteCallSiteIds(offset, /*reserve_only*/ true); + offset += WriteMethodHandles(offset); + + uint32_t data_offset_ = 0u; + if (compute_offsets_) { + // Data section. + offset = RoundUp(offset, kDataSectionAlignment); + data_offset_ = offset; + } + + // Write code item first to minimize the space required for encoded methods. + // For cdex, the code items don't depend on the debug info. + offset += WriteCodeItems(offset, /*reserve_only*/ false); + + // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of + // the debug info offset table. + SortDebugInfosByMethodIndex(); + offset += WriteDebugInfoItems(offset); + + offset += WriteEncodedArrays(offset); + offset += WriteAnnotations(offset); + offset += WriteAnnotationSets(offset); + offset += WriteAnnotationSetRefs(offset); + offset += WriteAnnotationsDirectories(offset); + offset += WriteTypeLists(offset); + offset += WriteClassDatas(offset); + offset += WriteStringDatas(offset); + + // Write delayed id sections that depend on data sections. + WriteStringIds(string_ids_offset, /*reserve_only*/ false); + WriteProtoIds(proto_ids_offset, /*reserve_only*/ false); + WriteClassDefs(class_defs_offset, /*reserve_only*/ false); + WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false); + + // Write the map list. + if (compute_offsets_) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList)); + collection.SetMapListOffset(offset); + } else { + offset = collection.MapListOffset(); + } + offset += GenerateAndWriteMapItems(offset); + offset = RoundUp(offset, kDataSectionAlignment); + + // Map items are included in the data section. + if (compute_offsets_) { + header_->SetDataSize(offset - data_offset_); + if (header_->DataSize() != 0) { + // Offset must be zero when the size is zero. + header_->SetDataOffset(data_offset_); + } else { + header_->SetDataOffset(0u); + } + } + + // Write link data if it exists. + const std::vector<uint8_t>& link_data = collection.LinkData(); + if (link_data.size() > 0) { + CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size())); + if (compute_offsets_) { + header_->SetLinkOffset(offset); + } + offset += Write(&link_data[0], link_data.size(), header_->LinkOffset()); + } + + // Write debug info offset table last to make dex file verifier happy. + offset += WriteDebugInfoOffsetTable(offset); + + // Write header last. + if (compute_offsets_) { + header_->SetFileSize(offset); + } + WriteHeader(); + + if (dex_layout_->GetOptions().update_checksum_) { + header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset)); + // Rewrite the header with the calculated checksum. + WriteHeader(); + } +} + } // namespace art diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index d13333bb18..37f6ff11a0 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -33,13 +33,30 @@ class CompactDexWriter : public DexWriter { compact_dex_level_(compact_dex_level) {} protected: + void WriteMemMap() OVERRIDE; + void WriteHeader() OVERRIDE; size_t GetHeaderSize() const OVERRIDE; + uint32_t WriteDebugInfoOffsetTable(uint32_t offset); + const CompactDexLevel compact_dex_level_; + uint32_t WriteCodeItem(dex_ir::CodeItem* code_item, uint32_t offset, bool reserve_only) OVERRIDE; + + void SortDebugInfosByMethodIndex(); + private: + // Position in the compact dex file for the debug info table data starts. + uint32_t debug_info_offsets_pos_ = 0u; + + // Offset into the debug info table data where the lookup table is. + uint32_t debug_info_offsets_table_offset_ = 0u; + + // Base offset of where debug info starts in the dex file. + uint32_t debug_info_base_ = 0u; + DISALLOW_COPY_AND_ASSIGN(CompactDexWriter); }; diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 2191ea601f..0a59cc9ba2 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -566,8 +566,10 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( } CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, - const DexFile::CodeItem& disk_code_item, uint32_t offset) { - CodeItemDebugInfoAccessor accessor(dex_file, &disk_code_item); + const DexFile::CodeItem& disk_code_item, + uint32_t offset, + uint32_t dex_method_index) { + CodeItemDebugInfoAccessor accessor(dex_file, &disk_code_item, dex_method_index); const uint16_t registers_size = accessor.RegistersSize(); const uint16_t ins_size = accessor.InsSize(); const uint16_t outs_size = accessor.OutsSize(); @@ -705,7 +707,10 @@ MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataIt DebugInfoItem* debug_info = nullptr; if (disk_code_item != nullptr) { if (code_item == nullptr) { - code_item = CreateCodeItem(dex_file, *disk_code_item, cdii.GetMethodCodeItemOffset()); + code_item = CreateCodeItem(dex_file, + *disk_code_item, + cdii.GetMethodCodeItemOffset(), + cdii.GetMemberIndex()); } debug_info = code_item->DebugInfo(); } diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 6797fa5dd6..ca47b348f1 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -133,6 +133,7 @@ template<class T> class CollectionVector : public CollectionBase<T> { uint32_t Size() const { return collection_.size(); } Vector& Collection() { return collection_; } + const Vector& Collection() const { return collection_; } protected: Vector collection_; @@ -230,6 +231,8 @@ class Collections { CollectionVector<CodeItem>::Vector& CodeItems() { return code_items_.Collection(); } CollectionVector<ClassData>::Vector& ClassDatas() { return class_datas_.Collection(); } + const CollectionVector<ClassDef>::Vector& ClassDefs() const { return class_defs_.Collection(); } + void CreateStringId(const DexFile& dex_file, uint32_t i); void CreateTypeId(const DexFile& dex_file, uint32_t i); void CreateProtoId(const DexFile& dex_file, uint32_t i); @@ -251,8 +254,10 @@ class Collections { const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset); AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset); - CodeItem* CreateCodeItem( - const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset); + CodeItem* CreateCodeItem(const DexFile& dex_file, + const DexFile::CodeItem& disk_code_item, + uint32_t offset, + uint32_t dex_method_index); ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset); void AddAnnotationsFromMapListSection(const DexFile& dex_file, uint32_t start_offset, diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 489a6b15ba..6e1cb62f0b 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -30,25 +30,6 @@ namespace art { -static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; -static constexpr uint32_t kDexSectionWordAlignment = 4; - -static constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { - switch (type) { - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - return alignof(uint8_t); - - default: - // All other sections are kDexAlignedSection. - return kDexSectionWordAlignment; - } -} - - size_t EncodeIntValue(int32_t value, uint8_t* buffer) { size_t length = 0; if (value >= 0) { @@ -526,69 +507,96 @@ uint32_t DexWriter::WriteDebugInfoItems(uint32_t offset) { return offset - start; } +uint32_t DexWriter::WriteCodeItemPostInstructionData(dex_ir::CodeItem* code_item, + uint32_t offset, + bool reserve_only) { + const uint32_t start_offset = offset; + if (code_item->TriesSize() != 0) { + // Make sure the try items are properly aligned. + offset = RoundUp(offset, kDexTryItemAlignment); + // Write try items. + for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { + DexFile::TryItem disk_try_item; + if (!reserve_only) { + disk_try_item.start_addr_ = try_item->StartAddr(); + disk_try_item.insn_count_ = try_item->InsnCount(); + disk_try_item.handler_off_ = try_item->GetHandlers()->GetListOffset(); + } + offset += Write(&disk_try_item, sizeof(disk_try_item), offset); + } + size_t max_offset = offset; + // Leave offset pointing to the end of the try items. + UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); + for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { + size_t list_offset = offset + handlers->GetListOffset(); + uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : + handlers->GetHandlers()->size(); + list_offset += WriteSleb128(size, list_offset); + for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { + if (handler->GetTypeId() != nullptr) { + list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset); + } + list_offset += WriteUleb128(handler->GetAddress(), list_offset); + } + // TODO: Clean this up to write the handlers in address order. + max_offset = std::max(max_offset, list_offset); + } + offset = max_offset; + } + + return offset - start_offset; +} + +uint32_t DexWriter::WriteCodeItem(dex_ir::CodeItem* code_item, + uint32_t offset, + bool reserve_only) { + DCHECK(code_item != nullptr); + const uint32_t start_offset = offset; + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); + ProcessOffset(&offset, code_item); + + StandardDexFile::CodeItem disk_code_item; + if (!reserve_only) { + disk_code_item.registers_size_ = code_item->RegistersSize(); + disk_code_item.ins_size_ = code_item->InsSize(); + disk_code_item.outs_size_ = code_item->OutsSize(); + disk_code_item.tries_size_ = code_item->TriesSize(); + disk_code_item.debug_info_off_ = code_item->DebugInfo() == nullptr + ? 0 + : code_item->DebugInfo()->GetOffset(); + disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize(); + } + // Avoid using sizeof so that we don't write the fake instruction array at the end of the code + // item. + offset += Write(&disk_code_item, + OFFSETOF_MEMBER(StandardDexFile::CodeItem, insns_), + offset); + // Write the instructions. + offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); + // Write the post instruction data. + offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only); + return offset - start_offset; +} + uint32_t DexWriter::WriteCodeItems(uint32_t offset, bool reserve_only) { DexLayoutSection* code_section = nullptr; if (!reserve_only && dex_layout_ != nullptr) { code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>( DexLayoutSections::SectionType::kSectionTypeCode)]; } - uint16_t uint16_buffer[4] = {}; - uint32_t uint32_buffer[2] = {}; uint32_t start = offset; for (auto& code_item : header_->GetCollections().CodeItems()) { - offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); - ProcessOffset(&offset, code_item.get()); - if (!reserve_only) { - uint16_buffer[0] = code_item->RegistersSize(); - uint16_buffer[1] = code_item->InsSize(); - uint16_buffer[2] = code_item->OutsSize(); - uint16_buffer[3] = code_item->TriesSize(); - uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : - code_item->DebugInfo()->GetOffset(); - uint32_buffer[1] = code_item->InsnsSize(); - // Only add the section hotness info once. - if (code_section != nullptr) { - auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); - if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { - code_section->parts_[static_cast<size_t>(it->second)].CombineSection( - code_item->GetOffset(), code_item->GetOffset() + code_item->GetSize()); - } - } - } - offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset); - offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); - offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); - if (code_item->TriesSize() != 0) { - if (code_item->InsnsSize() % 2 != 0) { - uint16_t padding[1] = { 0 }; - offset += Write(padding, sizeof(uint16_t), offset); - } - uint32_t start_addr[1]; - uint16_t insn_count_and_handler_off[2]; - for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { - start_addr[0] = try_item->StartAddr(); - insn_count_and_handler_off[0] = try_item->InsnCount(); - insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset(); - offset += Write(start_addr, sizeof(uint32_t), offset); - offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset); - } - // Leave offset pointing to the end of the try items. - UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); - for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { - size_t list_offset = offset + handlers->GetListOffset(); - uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : - handlers->GetHandlers()->size(); - list_offset += WriteSleb128(size, list_offset); - for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { - if (handler->GetTypeId() != nullptr) { - list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset); - } - list_offset += WriteUleb128(handler->GetAddress(), list_offset); - } + const size_t code_item_size = WriteCodeItem(code_item.get(), offset, reserve_only); + // Only add the section hotness info once. + if (!reserve_only && code_section != nullptr) { + auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); + if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { + code_section->parts_[static_cast<size_t>(it->second)].CombineSection( + offset, + offset + code_item_size); } } - // TODO: Clean this up to properly calculate the size instead of assuming it doesn't change. - offset = code_item->GetOffset() + code_item->GetSize(); + offset += code_item_size; } if (compute_offsets_ && start != offset) { diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index 92a002edc7..fdeb299aa4 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -23,6 +23,7 @@ #include "base/unix_file/fd_file.h" #include "dex/compact_dex_level.h" +#include "dex/dex_file.h" #include "dex_ir.h" #include "mem_map.h" #include "os.h" @@ -59,6 +60,25 @@ class MapItemQueue : public class DexWriter { public: + static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; + static constexpr uint32_t kDexSectionWordAlignment = 4; + static constexpr uint32_t kDexTryItemAlignment = sizeof(uint32_t); + + static inline constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { + switch (type) { + case DexFile::kDexTypeClassDataItem: + case DexFile::kDexTypeStringDataItem: + case DexFile::kDexTypeDebugInfoItem: + case DexFile::kDexTypeAnnotationItem: + case DexFile::kDexTypeEncodedArrayItem: + return alignof(uint8_t); + + default: + // All other sections are kDexAlignedSection. + return DexWriter::kDexSectionWordAlignment; + } + } + DexWriter(dex_ir::Header* header, MemMap* mem_map, DexLayout* dex_layout, @@ -77,7 +97,7 @@ class DexWriter { virtual ~DexWriter() {} protected: - void WriteMemMap(); + virtual void WriteMemMap(); size_t Write(const void* buffer, size_t length, size_t offset) WARN_UNUSED; size_t WriteSleb128(uint32_t value, size_t offset) WARN_UNUSED; @@ -118,6 +138,11 @@ class DexWriter { uint32_t WriteMapItems(uint32_t offset, MapItemQueue* queue); uint32_t GenerateAndWriteMapItems(uint32_t offset); + virtual uint32_t WriteCodeItemPostInstructionData(dex_ir::CodeItem* item, + uint32_t offset, + bool reserve_only); + virtual uint32_t WriteCodeItem(dex_ir::CodeItem* item, uint32_t offset, bool reserve_only); + // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the // existing offset and use that for writing. void ProcessOffset(uint32_t* const offset, dex_ir::Item* item) { diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index e452e98c7c..1ced8ca771 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -101,7 +101,7 @@ static void dumpMethod(const DexFile* pDexFile, if (pCode == nullptr || codeOffset == 0) { return; } - CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx); // Method information. const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6668dace89..f53846c04d 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -718,7 +718,6 @@ class OatDumper { } vdex_file->Unquicken(MakeNonOwningPointerVector(tmp_dex_files), - vdex_file->GetQuickeningInfo(), /* decompile_return_instruction */ true); *dex_files = std::move(tmp_dex_files); diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index 963c6f8444..dcc834abe9 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -63,8 +63,7 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile& if (vdex == nullptr) { return; } - art::VdexFile::UnquickenDexFile( - new_dex_file, vdex->GetQuickeningInfo(), /* decompile_return_instruction */true); + vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true); } std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) { diff --git a/runtime/Android.bp b/runtime/Android.bp index 78cb3b6165..2e34bafd54 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -25,6 +25,7 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "dex/compact_dex_debug_info.cc", "dex/compact_dex_file.cc", "dex/dex_file.cc", "dex/dex_file_exception_helpers.cc", @@ -120,6 +121,7 @@ cc_defaults { "common_throws.cc", "compiler_filter.cc", "debugger.cc", + "dex/compact_dex_debug_info.cc", "dex/compact_dex_file.cc", "dex/dex_file.cc", "dex/dex_file_annotations.cc", @@ -637,6 +639,7 @@ art_cc_test { "class_table_test.cc", "compiler_filter_test.cc", "dex/code_item_accessors_test.cc", + "dex/compact_dex_debug_info_test.cc", "dex/compact_dex_file_test.cc", "dex/dex_file_test.cc", "dex/dex_file_verifier_test.cc", diff --git a/runtime/art_method.cc b/runtime/art_method.cc index f9eedae23e..db7289a632 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -577,14 +577,14 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param return true; } -const uint8_t* ArtMethod::GetQuickenedInfo() { +ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() { const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return nullptr; + return ArrayRef<const uint8_t>(); } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( - dex_file, GetCodeItemOffset()); + return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf(dex_file, + GetDexMethodIndex()); } const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { diff --git a/runtime/art_method.h b/runtime/art_method.h index c4a586ed92..cd06354859 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -21,6 +21,7 @@ #include <android-base/logging.h> +#include "base/array_ref.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" @@ -662,7 +663,7 @@ class ArtMethod FINAL { return hotness_count_; } - const uint8_t* GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); + ArrayRef<const uint8_t> GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method header for the compiled code containing 'pc'. Note that runtime // methods will return null for this method, as they are not oat based. diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h index 2792dc0663..63fd120991 100644 --- a/runtime/dex/code_item_accessors-inl.h +++ b/runtime/dex/code_item_accessors-inl.h @@ -34,15 +34,9 @@ inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) : CodeItemDataAccessor(*method->GetDexFile(), method->GetCodeItem()) {} inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) - : CodeItemDebugInfoAccessor(*method->GetDexFile(), method->GetCodeItem()) {} - -inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return; - } - Init(dex_file, code_item, OatFile::GetDebugInfoOffset(dex_file, code_item->debug_info_off_)); -} + : CodeItemDebugInfoAccessor(*method->GetDexFile(), + method->GetCodeItem(), + method->GetDexMethodIndex()) {} } // namespace art diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h index baea856e71..aaa86d4b14 100644 --- a/runtime/dex/code_item_accessors-no_art-inl.h +++ b/runtime/dex/code_item_accessors-no_art-inl.h @@ -146,22 +146,28 @@ inline const void* CodeItemDataAccessor::CodeItemDataEnd() const { inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t debug_info_offset) { + uint32_t dex_method_index) { + if (code_item == nullptr) { + return; + } dex_file_ = &dex_file; - debug_info_offset_ = debug_info_offset; if (dex_file.IsCompactDexFile()) { - Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); + Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index); } else { DCHECK(dex_file.IsStandardDexFile()); Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); } } -inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { +inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item, + uint32_t dex_method_index) { + debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset( + dex_method_index); CodeItemDataAccessor::Init(code_item); } inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { + debug_info_offset_ = code_item.debug_info_off_; CodeItemDataAccessor::Init(code_item); } diff --git a/runtime/dex/code_item_accessors.h b/runtime/dex/code_item_accessors.h index b5a6957548..66531f96bc 100644 --- a/runtime/dex/code_item_accessors.h +++ b/runtime/dex/code_item_accessors.h @@ -131,20 +131,16 @@ class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { public: CodeItemDebugInfoAccessor() = default; - // Handles null code items, but not null dex files. - ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item); - // Initialize with an existing offset. ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t debug_info_offset) { - Init(dex_file, code_item, debug_info_offset); + uint32_t dex_method_index) { + Init(dex_file, code_item, dex_method_index); } ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t debug_info_offset); + uint32_t dex_method_index); ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); @@ -159,7 +155,7 @@ class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { void* context) const; protected: - ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index); ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); private: diff --git a/runtime/dex/compact_dex_debug_info.cc b/runtime/dex/compact_dex_debug_info.cc new file mode 100644 index 0000000000..19495ca92c --- /dev/null +++ b/runtime/dex/compact_dex_debug_info.cc @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compact_dex_debug_info.h" + +#include "compact_dex_utils.h" +#include "leb128.h" + +namespace art { + +constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex; + +CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin, + uint32_t debug_info_base, + uint32_t debug_info_table_offset) + : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)), + debug_info_base_(debug_info_base), + data_begin_(data_begin) {} + +uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const { + const uint32_t offset = table_[method_idx / kElementsPerIndex]; + const size_t bit_index = method_idx % kElementsPerIndex; + + const uint8_t* block = data_begin_ + offset; + uint16_t bit_mask = *block; + ++block; + bit_mask = (bit_mask << kBitsPerByte) | *block; + ++block; + if ((bit_mask & (1 << bit_index)) == 0) { + // Bit is not set means the offset is 0 for the debug info. + return 0u; + } + // Trim off the bits above the index we want and count how many bits are set. This is how many + // lebs we need to decode. + size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index)); + DCHECK_GT(count, 0u); + uint32_t current_offset = debug_info_base_; + do { + current_offset += DecodeUnsignedLeb128(&block); + --count; + } while (count > 0); + return current_offset; +} + +void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets, + std::vector<uint8_t>* out_data, + uint32_t* out_min_offset, + uint32_t* out_table_offset) { + DCHECK(out_data != nullptr); + DCHECK(out_data->empty()); + // Calculate the base offset and return it. + *out_min_offset = std::numeric_limits<uint32_t>::max(); + for (const uint32_t offset : debug_info_offsets) { + if (offset != 0u) { + *out_min_offset = std::min(*out_min_offset, offset); + } + } + // Write the leb blocks and store the important offsets (each kElementsPerIndex elements). + size_t block_start = 0; + + std::vector<uint32_t> offset_table; + + // Write data first then the table. + while (block_start < debug_info_offsets.size()) { + // Write the offset of the block for each block. + offset_table.push_back(out_data->size()); + + // Block size of up to kElementsPerIndex + const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex); + + // Calculate bit mask since need to write that first. + uint16_t bit_mask = 0u; + for (size_t i = 0; i < block_size; ++i) { + if (debug_info_offsets[block_start + i] != 0u) { + bit_mask |= 1 << i; + } + } + // Write bit mask. + out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte)); + out_data->push_back(static_cast<uint8_t>(bit_mask)); + + // Write debug info offsets relative to the current offset. + uint32_t current_offset = *out_min_offset; + for (size_t i = 0; i < block_size; ++i) { + const uint32_t debug_info_offset = debug_info_offsets[block_start + i]; + if (debug_info_offset != 0u) { + uint32_t delta = debug_info_offset - current_offset; + EncodeUnsignedLeb128(out_data, delta); + current_offset = debug_info_offset; + } + } + + block_start += block_size; + } + + // Write the offset table. + AlignmentPadVector(out_data, alignof(uint32_t)); + *out_table_offset = out_data->size(); + out_data->insert(out_data->end(), + reinterpret_cast<const uint8_t*>(&offset_table[0]), + reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size())); +} + +} // namespace art diff --git a/runtime/dex/compact_dex_debug_info.h b/runtime/dex/compact_dex_debug_info.h new file mode 100644 index 0000000000..1aff75879e --- /dev/null +++ b/runtime/dex/compact_dex_debug_info.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ +#define ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ + +#include <cstdint> +#include <vector> + +namespace art { + +// Debug offset table for compact dex, aims to minimize size while still providing reasonable +// speed (10-20ns average time per lookup on host). +class CompactDexDebugInfoOffsetTable { + public: + // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the + // integer is modified. + static constexpr size_t kElementsPerIndex = 16; + + // Leb block format: + // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk. + // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset + // changes compared to the previous index. + + class Accessor { + public: + Accessor(const uint8_t* data_begin, + uint32_t debug_info_base, + uint32_t debug_info_table_offset); + + // Return the debug info for a method index (or 0 if it doesn't have one). + uint32_t GetDebugInfoOffset(uint32_t method_idx) const; + + private: + const uint32_t* const table_; + const uint32_t debug_info_base_; + const uint8_t* const data_begin_; + }; + + // Returned offsets are all relative to debug_info_offsets. + static void Build(const std::vector<uint32_t>& debug_info_offsets, + std::vector<uint8_t>* out_data, + uint32_t* out_min_offset, + uint32_t* out_table_offset); + + // 32 bit aligned for the offset table. + static constexpr size_t kAlignment = sizeof(uint32_t); +}; + +} // namespace art + +#endif // ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ diff --git a/runtime/dex/compact_dex_debug_info_test.cc b/runtime/dex/compact_dex_debug_info_test.cc new file mode 100644 index 0000000000..02b95e68d7 --- /dev/null +++ b/runtime/dex/compact_dex_debug_info_test.cc @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vector> +#include <sys/mman.h> + +#include "base/logging.h" +#include "dex/compact_dex_debug_info.h" +#include "gtest/gtest.h" +#include "mem_map.h" + +namespace art { + +TEST(CompactDexDebugInfoTest, TestBuildAndAccess) { + MemMap::Init(); + + const size_t kDebugInfoMinOffset = 1234567; + std::vector<uint32_t> offsets = { + 0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0, + std::numeric_limits<uint32_t>::max() - kDebugInfoMinOffset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + }; + // Add some large offset since the debug info section will never be that close to the beginning + // of the file. + for (uint32_t& offset : offsets) { + if (offset != 0u) { + offset += kDebugInfoMinOffset; + } + } + + std::vector<uint8_t> data; + uint32_t base_offset = 0; + uint32_t table_offset = 0; + CompactDexDebugInfoOffsetTable::Build(offsets, + /*out*/ &data, + /*out*/ &base_offset, + /*out*/ &table_offset); + EXPECT_GE(base_offset, kDebugInfoMinOffset); + EXPECT_LT(table_offset, data.size()); + ASSERT_GT(data.size(), 0u); + const size_t before_size = offsets.size() * sizeof(offsets.front()); + EXPECT_LT(data.size(), before_size); + + // Note that the accessor requires the data to be aligned. Use memmap to accomplish this. + std::string error_msg; + // Leave some extra room since we don't copy the table at the start (for testing). + constexpr size_t kExtraOffset = 4 * 128; + std::unique_ptr<MemMap> fake_dex(MemMap::MapAnonymous("fake dex", + nullptr, + data.size() + kExtraOffset, + PROT_READ | PROT_WRITE, + /*low_4gb*/ false, + /*reuse*/ false, + &error_msg)); + ASSERT_TRUE(fake_dex != nullptr) << error_msg; + std::copy(data.begin(), data.end(), fake_dex->Begin() + kExtraOffset); + + CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex->Begin() + kExtraOffset, + base_offset, + table_offset); + for (size_t i = 0; i < offsets.size(); ++i) { + EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i)); + } + + // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller + // for sorted increasing order. + std::sort(offsets.begin(), offsets.end()); + std::vector<uint8_t> sorted_data; + CompactDexDebugInfoOffsetTable::Build(offsets, + /*out*/ &sorted_data, + /*out*/ &base_offset, + /*out*/ &table_offset); + EXPECT_LT(sorted_data.size(), data.size()); + { + ScopedLogSeverity sls(LogSeverity::INFO); + LOG(INFO) << "raw size " << before_size + << " table size " << data.size() + << " sorted table size " << sorted_data.size(); + } +} + +} // namespace art diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc index 2d1ee0420e..ff193ffb07 100644 --- a/runtime/dex/compact_dex_file.cc +++ b/runtime/dex/compact_dex_file.cc @@ -63,4 +63,21 @@ uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { reinterpret_cast<uintptr_t>(&item); } +CompactDexFile::CompactDexFile(const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + DexFileContainer* container) + : DexFile(base, + size, + location, + location_checksum, + oat_dex_file, + container, + /*is_compact_dex*/ true), + debug_info_offsets_(Begin() + GetHeader().debug_info_offsets_pos_, + GetHeader().debug_info_base_, + GetHeader().debug_info_offsets_table_offset_) {} + } // namespace art diff --git a/runtime/dex/compact_dex_file.h b/runtime/dex/compact_dex_file.h index 280c6f70cc..af782a981a 100644 --- a/runtime/dex/compact_dex_file.h +++ b/runtime/dex/compact_dex_file.h @@ -19,6 +19,7 @@ #include "base/casts.h" #include "dex_file.h" +#include "dex/compact_dex_debug_info.h" namespace art { @@ -41,13 +42,45 @@ class CompactDexFile : public DexFile { private: uint32_t feature_flags_ = 0u; + // Position in the compact dex file for the debug info table data starts. + uint32_t debug_info_offsets_pos_ = 0u; + + // Offset into the debug info table data where the lookup table is. + uint32_t debug_info_offsets_table_offset_ = 0u; + + // Base offset of where debug info starts in the dex file. + uint32_t debug_info_base_ = 0u; + + friend class CompactDexFile; friend class CompactDexWriter; }; + // Like the standard code item except without a debug info offset. struct CodeItem : public DexFile::CodeItem { + static constexpr size_t kAlignment = sizeof(uint32_t); + private: - // TODO: Insert compact dex specific fields here. + CodeItem() = default; + + uint16_t registers_size_; // the number of registers used by this code + // (locals + parameters) + uint16_t ins_size_; // the number of words of incoming arguments to the method + // that this code is for + uint16_t outs_size_; // the number of words of outgoing argument space required + // by this code for method invocation + uint16_t tries_size_; // the number of try_items for this instance. If non-zero, + // then these appear as the tries array just after the + // insns in this instance. + + uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units + uint16_t insns_[1]; // actual array of bytecode. + + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; friend class CompactDexFile; + friend class CompactDexWriter; DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -73,25 +106,22 @@ class CompactDexFile : public DexFile { uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; + uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const { + return debug_info_offsets_.GetDebugInfoOffset(dex_method_index); + } + private: - // Not supported yet. CompactDexFile(const uint8_t* base, size_t size, const std::string& location, uint32_t location_checksum, const OatDexFile* oat_dex_file, - DexFileContainer* container) - : DexFile(base, - size, - location, - location_checksum, - oat_dex_file, - container, - /*is_compact_dex*/ true) {} + DexFileContainer* container); + + CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_; friend class DexFile; friend class DexFileLoader; - DISALLOW_COPY_AND_ASSIGN(CompactDexFile); }; diff --git a/runtime/dex/compact_dex_utils.h b/runtime/dex/compact_dex_utils.h new file mode 100644 index 0000000000..1c7e9514fd --- /dev/null +++ b/runtime/dex/compact_dex_utils.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ +#define ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ + +#include <vector> + +#include "base/bit_utils.h" + +namespace art { + +// Add padding to the end of the array until the size is aligned. +template <typename T, template<typename> class Allocator> +static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest, + size_t alignment) { + while (!IsAlignedParam(dest->size(), alignment)) { + dest->push_back(T()); + } +} + +} // namespace art + +#endif // ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h index c2a36ce01a..183d84e15d 100644 --- a/runtime/dex/dex_file.h +++ b/runtime/dex/dex_file.h @@ -301,43 +301,12 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem); }; - // Raw code_item. + // Base code_item, compact dex and standard dex have different code item layouts. struct CodeItem { - // Used when quickening / unquickening. - void SetDebugInfoOffset(uint32_t new_offset) { - debug_info_off_ = new_offset; - } - - uint32_t GetDebugInfoOffset() const { - return debug_info_off_; - } - protected: - uint16_t registers_size_; // the number of registers used by this code - // (locals + parameters) - uint16_t ins_size_; // the number of words of incoming arguments to the method - // that this code is for - uint16_t outs_size_; // the number of words of outgoing argument space required - // by this code for method invocation - uint16_t tries_size_; // the number of try_items for this instance. If non-zero, - // then these appear as the tries array just after the - // insns in this instance. - // Normally holds file offset to debug info stream. In case the method has been quickened - // holds an offset in the Vdex file containing both the actual debug_info_off and the - // quickening info offset. - // Don't use this field directly, use OatFile::GetDebugInfoOffset in general ART code, - // or DexFile::GetDebugInfoOffset in code that are not using a Runtime. - uint32_t debug_info_off_; - - uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units - uint16_t insns_[1]; // actual array of bytecode. + CodeItem() = default; private: - ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); - friend class CodeItemDataAccessor; - friend class CodeItemDebugInfoAccessor; - friend class CodeItemInstructionAccessor; - friend class VdexFile; // TODO: Remove this one when it's cleaned up. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -348,6 +317,8 @@ class DexFile { uint16_t handler_off_; private: + TryItem() = default; + friend class DexWriter; DISALLOW_COPY_AND_ASSIGN(TryItem); }; @@ -712,15 +683,6 @@ class DexFile { return reinterpret_cast<const CodeItem*>(addr); } - uint32_t GetDebugInfoOffset(const CodeItem* code_item) const { - if (code_item == nullptr) { - return 0; - } - CHECK(oat_dex_file_ == nullptr) - << "Should only use GetDebugInfoOffset in a non runtime setup"; - return code_item->GetDebugInfoOffset(); - } - const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; // Returns the number of prototype identifiers in the .dex file. diff --git a/runtime/dex/dex_file_test.cc b/runtime/dex/dex_file_test.cc index 1c8b3e4180..cb721af754 100644 --- a/runtime/dex/dex_file_test.cc +++ b/runtime/dex/dex_file_test.cc @@ -738,8 +738,10 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64( kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true); const DexFile::ClassDef& class_def = raw->GetClassDef(0); - const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); - CodeItemDebugInfoAccessor accessor(*raw, code_item); + constexpr uint32_t kMethodIdx = 1; + const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, + kMethodIdx)); + CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx); ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr)); } diff --git a/runtime/dex/standard_dex_file.h b/runtime/dex/standard_dex_file.h index 819b721ce9..6437def4f5 100644 --- a/runtime/dex/standard_dex_file.h +++ b/runtime/dex/standard_dex_file.h @@ -36,7 +36,27 @@ class StandardDexFile : public DexFile { static constexpr size_t kAlignment = 4; private: - // TODO: Insert standard dex specific fields here. + CodeItem() = default; + + uint16_t registers_size_; // the number of registers used by this code + // (locals + parameters) + uint16_t ins_size_; // the number of words of incoming arguments to the method + // that this code is for + uint16_t outs_size_; // the number of words of outgoing argument space required + // by this code for method invocation + uint16_t tries_size_; // the number of try_items for this instance. If non-zero, + // then these appear as the tries array just after the + // insns in this instance. + uint32_t debug_info_off_; // Holds file offset to debug info stream. + + uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units + uint16_t insns_[1]; // actual array of bytecode. + + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; + friend class DexWriter; friend class StandardDexFile; DISALLOW_COPY_AND_ASSIGN(CodeItem); }; diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index e1c07baede..7887191713 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -36,8 +36,7 @@ class DexDecompiler { const ArrayRef<const uint8_t>& quickened_info, bool decompile_return_instruction) : code_item_accessor_(dex_file, &code_item), - quicken_info_(quickened_info.data()), - quicken_info_number_of_indices_(QuickenInfoTable::NumberOfIndices(quickened_info.size())), + quicken_info_(quickened_info), decompile_return_instruction_(decompile_return_instruction) {} bool Decompile(); @@ -72,7 +71,7 @@ class DexDecompiler { } uint16_t NextIndex() { - DCHECK_LT(quicken_index_, quicken_info_number_of_indices_); + DCHECK_LT(quicken_index_, quicken_info_.NumIndices()); const uint16_t ret = quicken_info_.GetData(quicken_index_); quicken_index_++; return ret; @@ -80,7 +79,6 @@ class DexDecompiler { const CodeItemInstructionAccessor code_item_accessor_; const QuickenInfoTable quicken_info_; - const size_t quicken_info_number_of_indices_; const bool decompile_return_instruction_; size_t quicken_index_ = 0u; @@ -104,7 +102,7 @@ bool DexDecompiler::Decompile() { break; case Instruction::NOP: - if (quicken_info_number_of_indices_ > 0) { + if (quicken_info_.NumIndices() > 0) { // Only try to decompile NOP if there are more than 0 indices. Not having // any index happens when we unquicken a code item that only has // RETURN_VOID_NO_BARRIER as quickened instruction. @@ -181,14 +179,14 @@ bool DexDecompiler::Decompile() { } } - if (quicken_index_ != quicken_info_number_of_indices_) { + if (quicken_index_ != quicken_info_.NumIndices()) { if (quicken_index_ == 0) { LOG(WARNING) << "Failed to use any value in quickening info," << " potentially due to duplicate methods."; } else { LOG(FATAL) << "Failed to use all values in quickening info." << " Actual: " << std::hex << quicken_index_ - << " Expected: " << quicken_info_number_of_indices_; + << " Expected: " << quicken_info_.NumIndices(); return false; } } diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 446a004244..c03dbccbc4 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1535,21 +1535,6 @@ ArrayRef<GcRoot<mirror::Object>> OatFile::GetBssGcRoots() const { } } -uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off) { - // Note that although the specification says that 0 should be used if there - // is no debug information, some applications incorrectly use 0xFFFFFFFF. - // The following check also handles debug_info_off == 0. - if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) { - return debug_info_off; - } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return debug_info_off; - } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetDebugInfoOffset( - dex_file, debug_info_off); -} - const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, std::string* error_msg) const { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index e9f7edca61..bf22e0b88b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -115,10 +115,6 @@ class OatFile { const char* abs_dex_location, std::string* error_msg); - // Return the actual debug info offset for an offset that might be actually pointing to - // dequickening info. The returned debug info offset is the one originally in the the dex file. - static uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off); - virtual ~OatFile(); bool IsExecutable() const { diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h index ce11f3c19b..52eca61c06 100644 --- a/runtime/quicken_info.h +++ b/runtime/quicken_info.h @@ -17,15 +17,93 @@ #ifndef ART_RUNTIME_QUICKEN_INFO_H_ #define ART_RUNTIME_QUICKEN_INFO_H_ +#include "base/array_ref.h" #include "dex/dex_instruction.h" +#include "leb128.h" namespace art { -// QuickenInfoTable is a table of 16 bit dex indices. There is one slot fo every instruction that is -// possibly dequickenable. +// Table for getting the offset of quicken info. Doesn't have one slot for each index, so a +// combination of iteration and indexing is required to get the quicken info for a given dex method +// index. +class QuickenInfoOffsetTableAccessor { + public: + using TableType = uint32_t; + static constexpr uint32_t kElementsPerIndex = 16; + + class Builder { + public: + explicit Builder(std::vector<uint8_t>* out_data) : out_data_(out_data) {} + + void AddOffset(uint32_t index) { + out_data_->insert(out_data_->end(), + reinterpret_cast<const uint8_t*>(&index), + reinterpret_cast<const uint8_t*>(&index + 1)); + } + + private: + std::vector<uint8_t>* const out_data_; + }; + + // The table only covers every kElementsPerIndex indices. + static bool IsCoveredIndex(uint32_t index) { + return index % kElementsPerIndex == 0; + } + + explicit QuickenInfoOffsetTableAccessor(const uint8_t* data, uint32_t max_index) + : table_(reinterpret_cast<const uint32_t*>(data)), + num_indices_(RoundUp(max_index, kElementsPerIndex) / kElementsPerIndex) {} + + size_t SizeInBytes() const { + return NumIndices() * sizeof(table_[0]); + } + + uint32_t NumIndices() const { + return num_indices_; + } + + // Returns the offset for the index at or before the desired index. If the offset is for an index + // before the desired one, remainder is how many elements to traverse to reach the desired index. + TableType ElementOffset(uint32_t index, uint32_t* remainder) const { + *remainder = index % kElementsPerIndex; + return table_[index / kElementsPerIndex]; + } + + const uint8_t* DataEnd() const { + return reinterpret_cast<const uint8_t*>(table_ + NumIndices()); + } + + static uint32_t Alignment() { + return alignof(TableType); + } + + private: + const TableType* table_; + uint32_t num_indices_; +}; + +// QuickenInfoTable is a table of 16 bit dex indices. There is one slot for every instruction that +// is possibly dequickenable. class QuickenInfoTable { public: - explicit QuickenInfoTable(const uint8_t* data) : data_(data) {} + class Builder { + public: + Builder(std::vector<uint8_t>* out_data, size_t num_elements) : out_data_(out_data) { + EncodeUnsignedLeb128(out_data_, num_elements); + } + + void AddIndex(uint16_t index) { + out_data_->push_back(static_cast<uint8_t>(index)); + out_data_->push_back(static_cast<uint8_t>(index >> 8)); + } + + private: + std::vector<uint8_t>* const out_data_; + }; + + explicit QuickenInfoTable(ArrayRef<const uint8_t> data) + : data_(data.data()), + num_elements_(!data.empty() ? DecodeUnsignedLeb128(&data_) : 0u) {} bool IsNull() const { return data_ == nullptr; @@ -44,8 +122,18 @@ class QuickenInfoTable { return bytes / sizeof(uint16_t); } + static size_t SizeInBytes(ArrayRef<const uint8_t> data) { + QuickenInfoTable table(data); + return table.data_ + table.NumIndices() * 2 - data.data(); + } + + uint32_t NumIndices() const { + return num_elements_; + } + private: - const uint8_t* const data_; + const uint8_t* data_; + const uint32_t num_elements_; DISALLOW_COPY_AND_ASSIGN(QuickenInfoTable); }; diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index c16cfb6578..118cffeda6 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -29,6 +29,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex_to_dex_decompiler.h" +#include "quicken_info.h" namespace art { @@ -144,9 +145,8 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) { return nullptr; } - Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), - vdex->GetQuickeningInfo(), - /* decompile_return_instruction */ false); + vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), + /* decompile_return_instruction */ false); // Update the quickening info size to pretend there isn't any. reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0; } @@ -159,14 +159,15 @@ const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); if (cursor == nullptr) { // Beginning of the iteration, return the first dex file if there is one. - return HasDexSection() ? DexBegin() : nullptr; + return HasDexSection() ? DexBegin() + sizeof(QuickeningTableOffsetType) : nullptr; } else { // Fetch the next dex file. Return null if there is none. const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_; // Dex files are required to be 4 byte aligned. the OatWriter makes sure they are, see // OatWriter::SeekToDexFiles. data = AlignUp(data, 4); - return (data == DexEnd()) ? nullptr : data; + + return (data == DexEnd()) ? nullptr : data + sizeof(QuickeningTableOffsetType); } } @@ -197,64 +198,68 @@ bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_ return true; } -void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction) { - if (quickening_info.size() == 0 && !decompile_return_instruction) { - // Bail early if there is no quickening info and no need to decompile - // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. - return; - } - - for (uint32_t i = 0; i < dex_files.size(); ++i) { - UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction); +void VdexFile::Unquicken(const std::vector<const DexFile*>& target_dex_files, + bool decompile_return_instruction) const { + const uint8_t* source_dex = GetNextDexFileData(nullptr); + for (const DexFile* target_dex : target_dex_files) { + UnquickenDexFile(*target_dex, source_dex, decompile_return_instruction); + source_dex = GetNextDexFileData(source_dex); } + DCHECK(source_dex == nullptr); } -typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - -static uint32_t GetDebugInfoOffsetInternal(const DexFile& dex_file, - uint32_t offset_in_code_item, - const ArrayRef<const uint8_t>& quickening_info) { - if (quickening_info.size() == 0) { - // No quickening info: offset is the right one, return it. - return offset_in_code_item; - } - uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); - return *reinterpret_cast<const unaligned_uint32_t*>(quickening_info.data() + quickening_offset); +uint32_t VdexFile::GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const { + DCHECK_GE(source_dex_begin, DexBegin()); + DCHECK_LT(source_dex_begin, DexEnd()); + return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1]; } -static uint32_t GetQuickeningInfoOffsetFrom(const DexFile& dex_file, - uint32_t offset_in_code_item, - const ArrayRef<const uint8_t>& quickening_info) { - if (offset_in_code_item < dex_file.Size()) { - return VdexFile::kNoQuickeningInfoOffset; - } - if (quickening_info.size() == 0) { - // No quickening info. - return VdexFile::kNoQuickeningInfoOffset; - } - uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); +QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( + const uint8_t* source_dex_begin, + uint32_t num_method_ids, + const ArrayRef<const uint8_t>& quickening_info) const { + // The offset a is in preheader right before the dex file. + const uint32_t offset = GetQuickeningInfoTableOffset(source_dex_begin); + const uint8_t* data_ptr = quickening_info.data() + offset; + return QuickenInfoOffsetTableAccessor(data_ptr, num_method_ids); +} - // Add 2 * sizeof(uint32_t) for the debug info offset and the data offset. - CHECK_LE(quickening_offset + 2 * sizeof(uint32_t), quickening_info.size()); - return *reinterpret_cast<const unaligned_uint32_t*>( - quickening_info.data() + quickening_offset + sizeof(uint32_t)); +QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( + const DexFile& dex_file, + const ArrayRef<const uint8_t>& quickening_info) const { + return GetQuickenInfoOffsetTable(dex_file.Begin(), dex_file.NumMethodIds(), quickening_info); } static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info, uint32_t quickening_offset) { - return (quickening_offset == VdexFile::kNoQuickeningInfoOffset) - ? ArrayRef<const uint8_t>(nullptr, 0) - : quickening_info.SubArray( - quickening_offset + sizeof(uint32_t), - *reinterpret_cast<const unaligned_uint32_t*>( - quickening_info.data() + quickening_offset)); + ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset); + return remaining.SubArray(0u, QuickenInfoTable::SizeInBytes(remaining)); +} + +static uint32_t GetQuickeningInfoOffset(const QuickenInfoOffsetTableAccessor& table, + uint32_t dex_method_index, + const ArrayRef<const uint8_t>& quickening_info) { + DCHECK(!quickening_info.empty()); + uint32_t remainder; + uint32_t offset = table.ElementOffset(dex_method_index, &remainder); + // Decode the sizes for the remainder offsets (not covered by the table). + while (remainder != 0) { + offset += GetQuickeningInfoAt(quickening_info, offset).size(); + --remainder; + } + return offset; +} + +void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, + const DexFile& source_dex_file, + bool decompile_return_instruction) const { + UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction); } void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction) { + const uint8_t* source_dex_begin, + bool decompile_return_instruction) const { + ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); if (quickening_info.size() == 0 && !decompile_return_instruction) { // Bail early if there is no quickening info and no need to decompile // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. @@ -269,19 +274,20 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( - target_dex_file, code_item->debug_info_off_, quickening_info); - if (quickening_offset != VdexFile::kNoQuickeningInfoOffset) { - // If we have quickening data, put back the original debug_info_off. - const_cast<DexFile::CodeItem*>(code_item)->SetDebugInfoOffset( - GetDebugInfoOffsetInternal(target_dex_file, - code_item->debug_info_off_, - quickening_info)); + ArrayRef<const uint8_t> quicken_data; + if (!quickening_info.empty()) { + const uint32_t quickening_offset = GetQuickeningInfoOffset( + GetQuickenInfoOffsetTable(source_dex_begin, + target_dex_file.NumMethodIds(), + quickening_info), + class_it.GetMemberIndex(), + quickening_info); + quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset); } optimizer::ArtDecompileDEX( target_dex_file, *code_item, - GetQuickeningInfoAt(quickening_info, quickening_offset), + quicken_data, decompile_return_instruction); } } @@ -289,25 +295,17 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, } } -uint32_t VdexFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const { - return GetDebugInfoOffsetInternal(dex_file, offset_in_code_item, GetQuickeningInfo()); -} - -const uint8_t* VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, - uint32_t code_item_offset) const { +ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, + uint32_t dex_method_idx) const { ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); - uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( - dex_file, dex_file.GetCodeItem(code_item_offset)->debug_info_off_, quickening_info); - - return GetQuickeningInfoAt(quickening_info, quickening_offset).data(); -} - -bool VdexFile::CanEncodeQuickenedData(const DexFile& dex_file) { - // We are going to use the debug_info_off_ to signal there is - // quickened data, by putting a value greater than dex_file.Size(). So - // make sure we have some room in the offset by checking that we have at least - // half of the range of a uint32_t. - return dex_file.Size() <= (std::numeric_limits<uint32_t>::max() >> 1); + if (quickening_info.empty()) { + return ArrayRef<const uint8_t>(); + } + const uint32_t quickening_offset = GetQuickeningInfoOffset( + GetQuickenInfoOffsetTable(dex_file, quickening_info), + dex_method_idx, + quickening_info); + return GetQuickeningInfoAt(quickening_info, quickening_offset); } } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index f78335d347..4687a393e2 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -24,6 +24,7 @@ #include "base/macros.h" #include "mem_map.h" #include "os.h" +#include "quicken_info.h" namespace art { @@ -35,18 +36,17 @@ class DexFile; // File format: // VdexFile::Header fixed-length header // -// DEX[0] array of the input DEX files -// DEX[1] the bytecode may have been quickened +// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. +// DEX[0] array of the input DEX files, the bytecode may have been quickened. +// quicken_table_off[1] +// DEX[1] // ... // DEX[D] // VerifierDeps // uint8[D][] verification dependencies // QuickeningInfo // uint8[D][] quickening data -// unaligned_uint32_t[D][2][] table of offsets pair: -// uint32_t[0] contains original CodeItem::debug_info_off_ -// uint32_t[1] contains quickening data offset from the start -// of QuickeningInfo +// uint32[D][] quickening data offset tables class VdexFile { public: @@ -84,8 +84,8 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Lookup-friendly encoding for quickening info. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '1', '\0' }; + // Last update: Side table for debug info offsets in compact dex. + static constexpr uint8_t kVdexVersion[] = { '0', '1', '4', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; @@ -98,6 +98,7 @@ class VdexFile { }; typedef uint32_t VdexChecksum; + using QuickeningTableOffsetType = uint32_t; explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} @@ -204,29 +205,42 @@ class VdexFile { // `decompile_return_instruction` controls if RETURN_VOID_BARRIER instructions are // decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator // instead of the faster QuickeningInfoIterator. - static void Unquicken(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction); + // Always unquickens using the vdex dex files as the source for quicken tables. + void Unquicken(const std::vector<const DexFile*>& target_dex_files, + bool decompile_return_instruction) const; // Fully unquicken `target_dex_file` based on `quickening_info`. - static void UnquickenDexFile(const DexFile& target_dex_file, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction); + void UnquickenDexFile(const DexFile& target_dex_file, + const DexFile& source_dex_file, + bool decompile_return_instruction) const; - // Return the quickening info of the given code item. - const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const; + // Return the quickening info of a given method index (or null if it's empty). + ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file, + uint32_t dex_method_idx) const; - uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const; + private: + uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const; - static bool CanEncodeQuickenedData(const DexFile& dex_file); + // Source dex must be the in the vdex file. + void UnquickenDexFile(const DexFile& target_dex_file, + const uint8_t* source_dex_begin, + bool decompile_return_instruction) const; - static constexpr uint32_t kNoQuickeningInfoOffset = -1; + QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + const DexFile& dex_file, + const ArrayRef<const uint8_t>& quickening_info) const; + + QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + const uint8_t* source_dex_begin, + uint32_t num_method_ids, + const ArrayRef<const uint8_t>& quickening_info) const; - private: bool HasDexSection() const { return GetHeader().GetDexSize() != 0; } + bool ContainsDexFile(const DexFile& dex_file) const; + const uint8_t* DexBegin() const { return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); } @@ -235,8 +249,6 @@ class VdexFile { return DexBegin() + GetHeader().GetDexSize(); } - uint32_t GetDexFileIndex(const DexFile& dex_file) const; - std::unique_ptr<MemMap> mmap_; DISALLOW_COPY_AND_ASSIGN(VdexFile); |