summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/oat_writer.cc95
-rw-r--r--compiler/oat_writer.h5
-rw-r--r--dex2oat/dex2oat_test.cc160
-rw-r--r--dexlayout/dexlayout.cc38
-rw-r--r--dexlayout/dexlayout.h6
-rw-r--r--oatdump/oatdump.cc7
-rw-r--r--runtime/Android.bp4
-rw-r--r--runtime/dex_file_layout.cc83
-rw-r--r--runtime/dex_file_layout.h96
-rw-r--r--runtime/oat.h4
-rw-r--r--runtime/oat_file.cc39
-rw-r--r--runtime/oat_file.h14
-rw-r--r--runtime/oat_file_manager.cc10
-rw-r--r--runtime/utils.cc15
-rw-r--r--runtime/utils.h3
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_