diff options
-rw-r--r-- | compiler/oat_writer.cc | 95 | ||||
-rw-r--r-- | compiler/oat_writer.h | 5 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 160 | ||||
-rw-r--r-- | dexlayout/dexlayout.cc | 38 | ||||
-rw-r--r-- | dexlayout/dexlayout.h | 6 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 7 | ||||
-rw-r--r-- | runtime/Android.bp | 4 | ||||
-rw-r--r-- | runtime/dex_file_layout.cc | 83 | ||||
-rw-r--r-- | runtime/dex_file_layout.h | 96 | ||||
-rw-r--r-- | runtime/oat.h | 4 | ||||
-rw-r--r-- | runtime/oat_file.cc | 39 | ||||
-rw-r--r-- | runtime/oat_file.h | 14 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 10 | ||||
-rw-r--r-- | runtime/utils.cc | 15 | ||||
-rw-r--r-- | runtime/utils.h | 3 |
15 files changed, 549 insertions, 30 deletions
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index d7e3a28777..0b2d7f423b 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -62,6 +62,9 @@ namespace art { namespace { // anonymous namespace +// If we write dex layout info in the oat file. +static constexpr bool kWriteDexLayoutInfo = true; + typedef DexFile::Header __attribute__((aligned(1))) UnalignedDexFileHeader; const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) { @@ -288,10 +291,14 @@ class OatWriter::OatDexFile { uint32_t class_offsets_offset_; uint32_t lookup_table_offset_; uint32_t method_bss_mapping_offset_; + uint32_t dex_sections_layout_offset_; // Data to write to a separate section. dchecked_vector<uint32_t> class_offsets_; + // Dex section layout info to serialize. + DexLayoutSections dex_sections_layout_; + private: DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; @@ -362,6 +369,9 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCo size_oat_dex_file_offset_(0), size_oat_dex_file_class_offsets_offset_(0), size_oat_dex_file_lookup_table_offset_(0), + size_oat_dex_file_dex_layout_sections_offset_(0), + size_oat_dex_file_dex_layout_sections_(0), + size_oat_dex_file_dex_layout_sections_alignment_(0), size_oat_dex_file_method_bss_mapping_offset_(0), size_oat_lookup_table_alignment_(0), size_oat_lookup_table_(0), @@ -571,11 +581,16 @@ bool OatWriter::WriteAndOpenDexFiles( } } - // Write TypeLookupTables into OAT. + // Write type lookup tables into the oat file. if (!WriteTypeLookupTables(&checksum_updating_rodata, dex_files)) { return false; } + // Write dex layout sections into the oat file. + if (!WriteDexLayoutSections(&checksum_updating_rodata, dex_files)) { + return false; + } + *opened_dex_files_map = std::move(dex_files_map); *opened_dex_files = std::move(dex_files); write_state_ = WriteState::kPrepareLayout; @@ -2320,6 +2335,9 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_dex_file_offset_); DO_STAT(size_oat_dex_file_class_offsets_offset_); DO_STAT(size_oat_dex_file_lookup_table_offset_); + DO_STAT(size_oat_dex_file_dex_layout_sections_offset_); + DO_STAT(size_oat_dex_file_dex_layout_sections_); + DO_STAT(size_oat_dex_file_dex_layout_sections_alignment_); DO_STAT(size_oat_dex_file_method_bss_mapping_offset_); DO_STAT(size_oat_lookup_table_alignment_); DO_STAT(size_oat_lookup_table_); @@ -2808,6 +2826,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) { return false; } + oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); // Set the checksum of the new oat dex file to be the original file's checksum. oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); return true; @@ -3153,6 +3172,70 @@ bool OatWriter::WriteTypeLookupTables( return true; } +bool OatWriter::WriteDexLayoutSections( + OutputStream* oat_rodata, + const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) { + TimingLogger::ScopedTiming split(__FUNCTION__, timings_); + + if (!kWriteDexLayoutInfo) { + return true;; + } + + uint32_t expected_offset = oat_data_offset_ + oat_size_; + off_t actual_offset = oat_rodata->Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + PLOG(ERROR) << "Failed to seek to dex layout section offset section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << oat_rodata->GetLocation(); + return false; + } + + DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size()); + size_t rodata_offset = oat_size_; + for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) { + OatDexFile* oat_dex_file = &oat_dex_files_[i]; + DCHECK_EQ(oat_dex_file->dex_sections_layout_offset_, 0u); + + // Write dex layout section alignment bytes. + const size_t padding_size = + RoundUp(rodata_offset, alignof(DexLayoutSections)) - rodata_offset; + if (padding_size != 0u) { + std::vector<uint8_t> buffer(padding_size, 0u); + if (!oat_rodata->WriteFully(buffer.data(), padding_size)) { + PLOG(ERROR) << "Failed to write lookup table alignment padding." + << " File: " << oat_dex_file->GetLocation() + << " Output: " << oat_rodata->GetLocation(); + return false; + } + size_oat_dex_file_dex_layout_sections_alignment_ += padding_size; + rodata_offset += padding_size; + } + + DCHECK_ALIGNED(rodata_offset, alignof(DexLayoutSections)); + DCHECK_EQ(oat_data_offset_ + rodata_offset, + static_cast<size_t>(oat_rodata->Seek(0u, kSeekCurrent))); + DCHECK(oat_dex_file != nullptr); + if (!oat_rodata->WriteFully(&oat_dex_file->dex_sections_layout_, + sizeof(oat_dex_file->dex_sections_layout_))) { + PLOG(ERROR) << "Failed to write dex layout sections." + << " File: " << oat_dex_file->GetLocation() + << " Output: " << oat_rodata->GetLocation(); + return false; + } + oat_dex_file->dex_sections_layout_offset_ = rodata_offset; + size_oat_dex_file_dex_layout_sections_ += sizeof(oat_dex_file->dex_sections_layout_); + rodata_offset += sizeof(oat_dex_file->dex_sections_layout_); + } + oat_size_ = rodata_offset; + + if (!oat_rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing type dex layout sections." + << " File: " << oat_rodata->GetLocation(); + return false; + } + + return true; +} + bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { if (!kIsVdexEnabled) { return true; @@ -3252,6 +3335,7 @@ OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, class_offsets_offset_(0u), lookup_table_offset_(0u), method_bss_mapping_offset_(0u), + dex_sections_layout_offset_(0u), class_offsets_() { } @@ -3262,7 +3346,8 @@ size_t OatWriter::OatDexFile::SizeOf() const { + sizeof(dex_file_offset_) + sizeof(class_offsets_offset_) + sizeof(lookup_table_offset_) - + sizeof(method_bss_mapping_offset_); + + sizeof(method_bss_mapping_offset_) + + sizeof(dex_sections_layout_offset_); } bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) const { @@ -3305,6 +3390,12 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) cons } oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_); + if (!out->WriteFully(&dex_sections_layout_offset_, sizeof(dex_sections_layout_offset_))) { + PLOG(ERROR) << "Failed to write dex section layout info to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_dex_layout_sections_offset_ += sizeof(dex_sections_layout_offset_); + if (!out->WriteFully(&method_bss_mapping_offset_, sizeof(method_bss_mapping_offset_))) { PLOG(ERROR) << "Failed to write method bss mapping offset to " << out->GetLocation(); return false; diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 470d69edb3..8db00f76d0 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -324,6 +324,8 @@ class OatWriter { bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); bool WriteTypeLookupTables(OutputStream* oat_rodata, const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); + bool WriteDexLayoutSections(OutputStream* oat_rodata, + const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); bool WriteUpTo16BytesAlignment(OutputStream* out, uint32_t size, uint32_t* stat); void SetMultiOatRelativePatcherAdjustment(); @@ -455,6 +457,9 @@ class OatWriter { uint32_t size_oat_dex_file_offset_; uint32_t size_oat_dex_file_class_offsets_offset_; uint32_t size_oat_dex_file_lookup_table_offset_; + uint32_t size_oat_dex_file_dex_layout_sections_offset_; + uint32_t size_oat_dex_file_dex_layout_sections_; + uint32_t size_oat_dex_file_dex_layout_sections_alignment_; uint32_t size_oat_dex_file_method_bss_mapping_offset_; uint32_t size_oat_lookup_table_alignment_; uint32_t size_oat_lookup_table_; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 10efaf3d1b..0aa766c08a 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1188,4 +1188,164 @@ TEST_F(Dex2oatDeterminism, UnloadCompile) { EXPECT_GT(app_image_file->GetLength(), 0u); } +// Test that dexlayout section info is correctly written to the oat file for profile based +// compilation. +TEST_F(Dex2oatTest, LayoutSections) { + using Hotness = ProfileCompilationInfo::MethodHotness; + std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods")); + ScratchFile profile_file; + // We can only layout method indices with code items, figure out which ones have this property + // first. + std::vector<uint16_t> methods; + { + const DexFile::TypeId* type_id = dex->FindTypeId("LManyMethods;"); + dex::TypeIndex type_idx = dex->GetIndexForTypeId(*type_id); + const DexFile::ClassDef* class_def = dex->FindClassDef(type_idx); + ClassDataItemIterator it(*dex, dex->GetClassData(*class_def)); + it.SkipAllFields(); + std::set<size_t> code_item_offsets; + for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + const uint16_t method_idx = it.GetMemberIndex(); + const size_t code_item_offset = it.GetMethodCodeItemOffset(); + if (code_item_offsets.insert(code_item_offset).second) { + // Unique code item, add the method index. + methods.push_back(method_idx); + } + } + DCHECK(!it.HasNext()); + } + ASSERT_GE(methods.size(), 8u); + std::vector<uint16_t> hot_methods = {methods[1], methods[3], methods[5]}; + std::vector<uint16_t> startup_methods = {methods[1], methods[2], methods[7]}; + std::vector<uint16_t> post_methods = {methods[0], methods[2], methods[6]}; + // Here, we build the profile from the method lists. + ProfileCompilationInfo info; + info.AddMethodsForDex( + static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup), + dex.get(), + hot_methods.begin(), + hot_methods.end()); + info.AddMethodsForDex( + Hotness::kFlagStartup, + dex.get(), + startup_methods.begin(), + startup_methods.end()); + info.AddMethodsForDex( + Hotness::kFlagPostStartup, + dex.get(), + post_methods.begin(), + post_methods.end()); + for (uint16_t id : hot_methods) { + EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsHot()); + EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup()); + } + for (uint16_t id : startup_methods) { + EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup()); + } + for (uint16_t id : post_methods) { + EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsPostStartup()); + } + // Save the profile since we want to use it with dex2oat to produce an oat file. + ASSERT_TRUE(info.Save(profile_file.GetFd())); + // Generate a profile based odex. + const std::string dir = GetScratchDir(); + const std::string oat_filename = dir + "/base.oat"; + const std::string vdex_filename = dir + "/base.vdex"; + std::string error_msg; + const int res = GenerateOdexForTestWithStatus( + {dex->GetLocation()}, + oat_filename, + CompilerFilter::Filter::kQuicken, + &error_msg, + {"--profile-file=" + profile_file.GetFilename()}); + EXPECT_EQ(res, 0); + + // Open our generated oat file. + std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_filename.c_str(), + oat_filename.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex->GetLocation().c_str(), + &error_msg)); + ASSERT_TRUE(odex_file != nullptr); + std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); + ASSERT_EQ(oat_dex_files.size(), 1u); + // Check that the code sections match what we expect. + for (const OatDexFile* oat_dex : oat_dex_files) { + const DexLayoutSections* const sections = oat_dex->GetDexLayoutSections(); + // Testing of logging the sections. + ASSERT_TRUE(sections != nullptr); + LOG(INFO) << *sections; + + // Load the sections into temporary variables for convenience. + const DexLayoutSection& code_section = + sections->sections_[static_cast<size_t>(DexLayoutSections::SectionType::kSectionTypeCode)]; + const DexLayoutSection::Subsection& section_hot_code = + code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeHot)]; + const DexLayoutSection::Subsection& section_sometimes_used = + code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeSometimesUsed)]; + const DexLayoutSection::Subsection& section_startup_only = + code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeStartupOnly)]; + const DexLayoutSection::Subsection& section_unused = + code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeUnused)]; + + // All the sections should be non-empty. + EXPECT_GT(section_hot_code.size_, 0u); + EXPECT_GT(section_sometimes_used.size_, 0u); + EXPECT_GT(section_startup_only.size_, 0u); + EXPECT_GT(section_unused.size_, 0u); + + // Open the dex file since we need to peek at the code items to verify the layout matches what + // we expect. + std::unique_ptr<const DexFile> dex_file(oat_dex->OpenDexFile(&error_msg)); + ASSERT_TRUE(dex_file != nullptr) << error_msg; + const DexFile::TypeId* type_id = dex_file->FindTypeId("LManyMethods;"); + ASSERT_TRUE(type_id != nullptr); + dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id); + const DexFile::ClassDef* class_def = dex_file->FindClassDef(type_idx); + ASSERT_TRUE(class_def != nullptr); + + // Count how many code items are for each category, there should be at least one per category. + size_t hot_count = 0; + size_t post_startup_count = 0; + size_t startup_count = 0; + size_t unused_count = 0; + // Visit all of the methdos of the main class and cross reference the method indices to their + // corresponding code item offsets to verify the layout. + ClassDataItemIterator it(*dex_file, dex_file->GetClassData(*class_def)); + it.SkipAllFields(); + for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + const size_t method_idx = it.GetMemberIndex(); + const size_t code_item_offset = it.GetMethodCodeItemOffset(); + const bool is_hot = ContainsElement(hot_methods, method_idx); + const bool is_startup = ContainsElement(startup_methods, method_idx); + const bool is_post_startup = ContainsElement(post_methods, method_idx); + if (is_hot) { + // Hot is highest precedence, check that the hot methods are in the hot section. + EXPECT_LT(code_item_offset - section_hot_code.offset_, section_hot_code.size_); + ++hot_count; + } else if (is_post_startup) { + // Post startup is sometimes used section. + EXPECT_LT(code_item_offset - section_sometimes_used.offset_, section_sometimes_used.size_); + ++post_startup_count; + } else if (is_startup) { + // Startup at this point means not hot or post startup, these must be startup only then. + EXPECT_LT(code_item_offset - section_startup_only.offset_, section_startup_only.size_); + ++startup_count; + } else { + // If no flags are set, the method should be unused. + EXPECT_LT(code_item_offset - section_unused.offset_, section_unused.size_); + ++unused_count; + } + } + DCHECK(!it.HasNext()); + EXPECT_GT(hot_count, 0u); + EXPECT_GT(post_startup_count, 0u); + EXPECT_GT(startup_count, 0u); + EXPECT_GT(unused_count, 0u); + } +} + } // namespace art diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index fd92d77265..401a3ee284 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -34,6 +34,7 @@ #include "android-base/stringprintf.h" #include "dex_file-inl.h" +#include "dex_file_layout.h" #include "dex_file_verifier.h" #include "dex_instruction-inl.h" #include "dex_ir_builder.h" @@ -1680,21 +1681,13 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, } } - enum CodeItemState { - kCodeItemStateExecStartupOnly = 0, - kCodeItemStateHot, - kCodeItemStateClinit, - kCodeItemStateExec, - kCodeItemStateNotExecuted, - kCodeItemStateSize, - }; - static constexpr InvokeType invoke_types[] = { kDirect, kVirtual }; - std::unordered_set<dex_ir::CodeItem*> code_items[kCodeItemStateSize]; + const size_t num_layout_types = static_cast<size_t>(LayoutType::kLayoutTypeCount); + std::unordered_set<dex_ir::CodeItem*> code_items[num_layout_types]; for (InvokeType invoke_type : invoke_types) { for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) { const bool is_profile_class = @@ -1719,20 +1712,20 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, const bool is_startup_clinit = is_profile_class && is_clinit; using Hotness = ProfileCompilationInfo::MethodHotness; Hotness hotness = info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex())); - CodeItemState state = kCodeItemStateNotExecuted; + LayoutType state = LayoutType::kLayoutTypeUnused; if (hotness.IsHot()) { // Hot code is compiled, maybe one day it won't be accessed. So lay it out together for // now. - state = kCodeItemStateHot; + state = LayoutType::kLayoutTypeHot; } else if (is_startup_clinit || hotness.GetFlags() == Hotness::kFlagStartup) { // Startup clinit or a method that only has the startup flag. - state = kCodeItemStateExecStartupOnly; + state = LayoutType::kLayoutTypeStartupOnly; } else if (is_clinit) { - state = kCodeItemStateClinit; + state = LayoutType::kLayoutTypeUsedOnce; } else if (hotness.IsInProfile()) { - state = kCodeItemStateExec; + state = LayoutType::kLayoutTypeSometimesUsed; } - code_items[state].insert(code_item); + code_items[static_cast<size_t>(state)].insert(code_item); } } } @@ -1741,10 +1734,11 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, int32_t total_diff = 0; // The relative placement has no effect on correctness; it is used to ensure // the layout is deterministic - for (std::unordered_set<dex_ir::CodeItem*>& code_items_set : code_items) { + for (size_t index = 0; index < num_layout_types; ++index) { + const std::unordered_set<dex_ir::CodeItem*>& code_items_set = code_items[index]; // diff is reset for each class of code items. int32_t diff = 0; - uint32_t start_offset = code_item_offset; + const uint32_t start_offset = code_item_offset; for (dex_ir::ClassData* data : new_class_data_order) { data->SetOffset(data->GetOffset() + diff); for (InvokeType invoke_type : invoke_types) { @@ -1763,9 +1757,13 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, } } } - for (size_t i = 0; i < kCodeItemStateSize; ++i) { + DexLayoutSection& code_section = dex_sections_.sections_[static_cast<size_t>( + DexLayoutSections::SectionType::kSectionTypeCode)]; + code_section.parts_[index].offset_ = start_offset; + code_section.parts_[index].size_ = code_item_offset - start_offset; + for (size_t i = 0; i < num_layout_types; ++i) { VLOG(dex) << "Code item layout bucket " << i << " count=" << code_items[i].size() - << " bytes=" << code_item_offset - start_offset; + << " bytes=" << code_section.parts_[i].size_; } total_diff += diff; } diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index ed011d6771..9f6e8a4122 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -26,6 +26,7 @@ #include <stdint.h> #include <stdio.h> +#include "dex_file_layout.h" #include "dex_ir.h" #include "mem_map.h" @@ -84,6 +85,10 @@ class DexLayout { MemMap* GetAndReleaseMemMap() { return mem_map_.release(); } + const DexLayoutSections& GetSections() const { + return dex_sections_; + } + private: void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item); void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset); @@ -129,6 +134,7 @@ class DexLayout { FILE* out_file_; dex_ir::Header* header_; std::unique_ptr<MemMap> mem_map_; + DexLayoutSections dex_sections_; DISALLOW_COPY_AND_ASSIGN(DexLayout); }; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index f8b1f5375a..6f833c6e69 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -500,6 +500,13 @@ class OatDumper { os << "Dex file data for " << dex_file->GetLocation() << "\n"; data.Dump(os); os << "\n"; + const DexLayoutSections* const layout_sections = oat_dex_file->GetDexLayoutSections(); + if (layout_sections != nullptr) { + os << "Layout data\n"; + os << *layout_sections; + os << "\n"; + } + cumulative.Add(data); } os << "Cumulative dex file data\n"; diff --git a/runtime/Android.bp b/runtime/Android.bp index d534542e3c..0f5a1a8079 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -55,8 +55,9 @@ cc_defaults { "compiler_filter.cc", "debugger.cc", "dex_file.cc", - "dex_file_tracking_registrar.cc", "dex_file_annotations.cc", + "dex_file_layout.cc", + "dex_file_tracking_registrar.cc", "dex_file_verifier.cc", "dex_instruction.cc", "dex_to_dex_decompiler.cc", @@ -439,6 +440,7 @@ gensrcs { "debugger.h", "base/unix_file/fd_file.h", "dex_file.h", + "dex_file_layout.h", "dex_instruction.h", "dex_instruction_utils.h", "gc_root.h", diff --git a/runtime/dex_file_layout.cc b/runtime/dex_file_layout.cc new file mode 100644 index 0000000000..4375d7f799 --- /dev/null +++ b/runtime/dex_file_layout.cc @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 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 "dex_file_layout.h" + +#include <sys/mman.h> + +#include "dex_file.h" +#include "utils.h" + +namespace art { + +void DexLayoutSection::Subsection::Madvise(const DexFile* dex_file, int advice) const { + DCHECK(dex_file != nullptr); + DCHECK_LE(size_, dex_file->Size()); + DCHECK_LE(offset_ + size_, dex_file->Size()); + MadviseLargestPageAlignedRegion(dex_file->Begin() + offset_, + dex_file->Begin() + offset_ + size_, + advice); +} + +void DexLayoutSections::Madvise(const DexFile* dex_file, MadviseState state) const { + // The dex file is already defaulted to random access everywhere. + for (const DexLayoutSection& section : sections_) { + switch (state) { + case MadviseState::kMadviseStateAtLoad: { + section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeStartupOnly)].Madvise( + dex_file, + MADV_WILLNEED); + section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeHot)].Madvise( + dex_file, + MADV_WILLNEED); + break; + } + case MadviseState::kMadviseStateFinishedLaunch: { + section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeStartupOnly)].Madvise( + dex_file, + MADV_DONTNEED); + break; + } + case MadviseState::kMadviseStateFinishedTrim: { + section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeSometimesUsed)].Madvise( + dex_file, + MADV_DONTNEED); + section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeUsedOnce)].Madvise( + dex_file, + MADV_DONTNEED); + break; + } + } + } +} + +std::ostream& operator<<(std::ostream& os, const DexLayoutSection& section) { + for (size_t i = 0; i < static_cast<size_t>(LayoutType::kLayoutTypeCount); ++i) { + const DexLayoutSection::Subsection& part = section.parts_[i]; + os << static_cast<LayoutType>(i) << "(" + << part.offset_ << "-" << part.offset_ + part.size_ << ") "; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const DexLayoutSections& sections) { + for (size_t i = 0; i < static_cast<size_t>(DexLayoutSections::SectionType::kSectionCount); ++i) { + os << static_cast<DexLayoutSections::SectionType>(i) << ":" << sections.sections_[i] << "\n"; + } + return os; +} + +} // namespace art diff --git a/runtime/dex_file_layout.h b/runtime/dex_file_layout.h new file mode 100644 index 0000000000..40cc91232e --- /dev/null +++ b/runtime/dex_file_layout.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2017 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_FILE_LAYOUT_H_ +#define ART_RUNTIME_DEX_FILE_LAYOUT_H_ + +#include <cstdint> +#include <iosfwd> + +namespace art { + +class DexFile; + +enum class LayoutType : uint8_t { + // Layout of things that are randomly used. These should be advised to random access. + // Without layout, this is the default mode when loading a dex file. + kLayoutTypeSometimesUsed, + // Layout of things that are only used during startup, these can be madvised after launch. + kLayoutTypeStartupOnly, + // Layout of things that are hot (commonly accessed), these should be pinned or madvised will + // need. + kLayoutTypeHot, + // Layout of things that are needed probably only once (class initializers). These can be + // madvised during trim events. + kLayoutTypeUsedOnce, + // Layout of things that are thought to be unused. These things should be advised to random + // access. + kLayoutTypeUnused, + // Unused value, just the number of elements in the enum. + kLayoutTypeCount, +}; +std::ostream& operator<<(std::ostream& os, const LayoutType& collector_type); + +enum class MadviseState : uint8_t { + // Madvise based on a file that was just loaded. + kMadviseStateAtLoad, + // Madvise based after launch is finished. + kMadviseStateFinishedLaunch, + // Trim by madvising code that is unlikely to be too important in the future. + kMadviseStateFinishedTrim, +}; +std::ostream& operator<<(std::ostream& os, const MadviseState& collector_type); + +// A dex layout section such as code items or strings. Each section is composed of subsections +// that are layed out ajacently to each other such as (hot, unused, startup, etc...). +class DexLayoutSection { + public: + // A subsection is a a continuous range of dex file that is all part of the same layout hint. + class Subsection { + public: + // Use uint32_t to handle 32/64 bit cross compilation. + uint32_t offset_ = 0u; + uint32_t size_ = 0u; + + void Madvise(const DexFile* dex_file, int advice) const; + }; + + Subsection parts_[static_cast<size_t>(LayoutType::kLayoutTypeCount)]; +}; + +// A set of dex layout sections, currently there is only one section for code and one for strings. +class DexLayoutSections { + public: + enum class SectionType : uint8_t { + kSectionTypeCode, + kSectionTypeStrings, + kSectionCount, + }; + + // Advise access about the dex file based on layout. The caller is expected to have already + // madvised to MADV_RANDOM. + void Madvise(const DexFile* dex_file, MadviseState state) const; + + DexLayoutSection sections_[static_cast<size_t>(SectionType::kSectionCount)]; +}; + +std::ostream& operator<<(std::ostream& os, const DexLayoutSections::SectionType& collector_type); +std::ostream& operator<<(std::ostream& os, const DexLayoutSection& section); +std::ostream& operator<<(std::ostream& os, const DexLayoutSections& sections); + +} // namespace art + +#endif // ART_RUNTIME_DEX_FILE_LAYOUT_H_ diff --git a/runtime/oat.h b/runtime/oat.h index c4a983e78b..1d79ed6dea 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: MIPS Baker thunks. - static constexpr uint8_t kOatVersion[] = { '1', '3', '1', '\0' }; + // Last oat version changed reason: Add dex section layout info to header. + static constexpr uint8_t kOatVersion[] = { '1', '3', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 4033f8cd3a..0af062280f 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -544,6 +544,19 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } + uint32_t dex_layout_sections_offset; + if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_layout_sections_offset))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " + "after dex layout sections offset", + GetLocation().c_str(), + i, + dex_file_location.c_str()); + return false; + } + const DexLayoutSections* const dex_layout_sections = dex_layout_sections_offset != 0 + ? reinterpret_cast<const DexLayoutSections*>(Begin() + dex_layout_sections_offset) + : nullptr; + uint32_t method_bss_mapping_offset; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &method_bss_mapping_offset))) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " @@ -635,7 +648,8 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { lookup_table_data, method_bss_mapping, class_offsets_pointer, - current_dex_cache_arrays); + current_dex_cache_arrays, + dex_layout_sections); oat_dex_files_storage_.push_back(oat_dex_file); // Add the location and canonical location (if different) to the oat_dex_files_ table. @@ -1362,7 +1376,8 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, const uint8_t* lookup_table_data, const MethodBssMapping* method_bss_mapping_data, const uint32_t* oat_class_offsets_pointer, - uint8_t* dex_cache_arrays) + uint8_t* dex_cache_arrays, + const DexLayoutSections* dex_layout_sections) : oat_file_(oat_file), dex_file_location_(dex_file_location), canonical_dex_file_location_(canonical_dex_file_location), @@ -1371,7 +1386,8 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, lookup_table_data_(lookup_table_data), method_bss_mapping_(method_bss_mapping_data), oat_class_offsets_pointer_(oat_class_offsets_pointer), - dex_cache_arrays_(dex_cache_arrays) { + dex_cache_arrays_(dex_cache_arrays), + dex_layout_sections_(dex_layout_sections) { // Initialize TypeLookupTable. if (lookup_table_data_ != nullptr) { // Peek the number of classes from the DexFile. @@ -1477,6 +1493,23 @@ const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_fi return nullptr; } +// Madvise the dex file based on the state we are moving to. +void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) { + if (state == MadviseState::kMadviseStateAtLoad) { + // Default every dex file to MADV_RANDOM when its loaded by default. + MadviseLargestPageAlignedRegion(dex_file.Begin(), + dex_file.Begin() + dex_file.Size(), + MADV_RANDOM); + } + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + if (oat_dex_file != nullptr) { + // Should always be there. + const DexLayoutSections* const sections = oat_dex_file->GetDexLayoutSections(); + CHECK(sections != nullptr); + sections->Madvise(&dex_file, state); + } +} + OatFile::OatClass::OatClass(const OatFile* oat_file, mirror::Class::Status status, OatClassType type, diff --git a/runtime/oat_file.h b/runtime/oat_file.h index be7d4953a0..9a7fe51e8e 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -26,6 +26,7 @@ #include "base/stringpiece.h" #include "compiler_filter.h" #include "dex_file.h" +#include "dex_file_layout.h" #include "method_bss_mapping.h" #include "mirror/class.h" #include "oat.h" @@ -38,6 +39,7 @@ namespace art { class BitVector; class ElfFile; +class DexLayoutSections; template <class MirrorType> class GcRoot; class MemMap; class OatDexFile; @@ -442,6 +444,9 @@ class OatDexFile FINAL { const char* descriptor, size_t hash); + // Madvise the dex file based on the state we are moving to. + static void MadviseDexFile(const DexFile& dex_file, MadviseState state); + TypeLookupTable* GetTypeLookupTable() const { return lookup_table_.get(); } @@ -451,6 +456,11 @@ class OatDexFile FINAL { // Create only with a type lookup table, used by the compiler to speed up compilation. explicit OatDexFile(std::unique_ptr<TypeLookupTable>&& lookup_table); + // Return the dex layout sections. + const DexLayoutSections* GetDexLayoutSections() const { + return dex_layout_sections_; + } + private: OatDexFile(const OatFile* oat_file, const std::string& dex_file_location, @@ -460,7 +470,8 @@ class OatDexFile FINAL { const uint8_t* lookup_table_data, const MethodBssMapping* method_bss_mapping, const uint32_t* oat_class_offsets_pointer, - uint8_t* dex_cache_arrays); + uint8_t* dex_cache_arrays, + const DexLayoutSections* dex_layout_sections); static void AssertAotCompiler(); @@ -474,6 +485,7 @@ class OatDexFile FINAL { const uint32_t* const oat_class_offsets_pointer_ = 0u; uint8_t* const dex_cache_arrays_ = nullptr; mutable std::unique_ptr<TypeLookupTable> lookup_table_; + const DexLayoutSections* const dex_layout_sections_ = nullptr; friend class OatFile; friend class OatFileBase; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 5baf59c665..de8f7ed684 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -48,9 +48,12 @@ namespace art { using android::base::StringPrintf; -// If true, then we attempt to load the application image if it exists. +// If true, we attempt to load the application image if it exists. static constexpr bool kEnableAppImage = true; +// If true, we advise the kernel about dex file mem map accesses. +static constexpr bool kMadviseDexFileAccesses = false; + const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) { WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); DCHECK(oat_file != nullptr); @@ -569,6 +572,11 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( } if (dex_files.empty()) { error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation()); + } else if (kMadviseDexFileAccesses) { + // Opened dex files from an oat file, madvise them to their loaded state. + for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { + OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad); + } } } diff --git a/runtime/utils.cc b/runtime/utils.cc index ffa9d45812..3fe18c7933 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -18,6 +18,7 @@ #include <inttypes.h> #include <pthread.h> +#include <sys/mman.h> // For madvise #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> @@ -940,4 +941,18 @@ void SleepForever() { } } +int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice) { + DCHECK_LE(begin, end); + begin = AlignUp(begin, kPageSize); + end = AlignDown(end, kPageSize); + if (begin < end) { + int result = madvise(const_cast<uint8_t*>(begin), end - begin, advice); + if (result != 0) { + PLOG(WARNING) << "madvise failed " << result; + } + return result; + } + return 0; +} + } // namespace art diff --git a/runtime/utils.h b/runtime/utils.h index f1f55766d6..739681d446 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -338,6 +338,9 @@ inline static int32_t Signum(T opnd) { return (opnd < 0) ? -1 : ((opnd == 0) ? 0 : 1); } +// Madvise the largest page aligned region within begin and end. +int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice); + } // namespace art #endif // ART_RUNTIME_UTILS_H_ |