Store layout info in dex files

Store layout info for code sections inside of the oat file. This will
be used to advise the kernel when dex files are loaded in
a follow up CL.

Added unit test in dex2oat_test.

Bug: 63178181
Test: test-art-host

(cherry-picked from commit 75c5ed6e75f70002db5fa7c609137c04dd2bdf40)

Change-Id: I4777506886bde42ff0affdac412a8395e8013a40
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index d7e3a28..0b2d7f4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -62,6 +62,9 @@
 
 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 @@
   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 @@
     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 @@
     }
   }
 
-  // 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 @@
     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 @@
   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 @@
   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 @@
       class_offsets_offset_(0u),
       lookup_table_offset_(0u),
       method_bss_mapping_offset_(0u),
+      dex_sections_layout_offset_(0u),
       class_offsets_() {
 }
 
@@ -3262,7 +3346,8 @@
           + 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 @@
   }
   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 470d69e..8db00f7 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -324,6 +324,8 @@
   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 @@
   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 10efaf3..0aa766c 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1188,4 +1188,164 @@
   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 fd92d77..401a3ee 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 @@
     }
   }
 
-  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 @@
         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 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 @@
         }
       }
     }
-    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 ed011d6..9f6e8a4 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 @@
 
   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 @@
   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 f8b1f53..6f833c6 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -500,6 +500,13 @@
       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 d534542..0f5a1a8 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -55,8 +55,9 @@
         "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 @@
         "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 0000000..4375d7f
--- /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 0000000..40cc912
--- /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 c4a983e..1d79ed6 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 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 4033f8c..0af0622 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -544,6 +544,19 @@
       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 @@
                                               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 @@
                                 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 @@
       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 @@
   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 be7d495..9a7fe51 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 @@
 
 class BitVector;
 class ElfFile;
+class DexLayoutSections;
 template <class MirrorType> class GcRoot;
 class MemMap;
 class OatDexFile;
@@ -442,6 +444,9 @@
                                                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 @@
   // 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 @@
              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 @@
   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 5baf59c..de8f7ed 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -48,9 +48,12 @@
 
 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 @@
     }
     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 ffa9d45..3fe18c7 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 @@
   }
 }
 
+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 f1f5576..739681d 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -338,6 +338,9 @@
   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_