Add owned section for CompactDex

The owned section is the part of the shared data section owned by
a given dex file. This enables efficiently attributing an offset to
a dex file.

Bug: 74443371
Bug: 63756964
Test: test-art-host

Change-Id: I2de9a281e18b02a20c3dcf5f484eacb591220cdc
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 094dfee..09ff14e 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1465,14 +1465,14 @@
 
 // Test that generating compact dex works.
 TEST_F(Dex2oatTest, GenerateCompactDex) {
-  std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
   // Generate a compact dex based odex.
   const std::string dir = GetScratchDir();
   const std::string oat_filename = dir + "/base.oat";
   const std::string vdex_filename = dir + "/base.vdex";
+  const std::string dex_location = GetTestDexFileName("MultiDex");
   std::string error_msg;
   const int res = GenerateOdexForTestWithStatus(
-      {dex->GetLocation()},
+      { dex_location },
       oat_filename,
       CompilerFilter::Filter::kQuicken,
       &error_msg,
@@ -1485,16 +1485,43 @@
                                                    nullptr,
                                                    false,
                                                    /*low_4gb*/false,
-                                                   dex->GetLocation().c_str(),
+                                                   dex_location.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 each dex is a compact dex.
+  ASSERT_GT(oat_dex_files.size(), 1u);
+  // Check that each dex is a compact dex file.
+  std::vector<std::unique_ptr<const CompactDexFile>> compact_dex_files;
   for (const OatDexFile* oat_dex : oat_dex_files) {
     std::unique_ptr<const DexFile> dex_file(oat_dex->OpenDexFile(&error_msg));
     ASSERT_TRUE(dex_file != nullptr) << error_msg;
     ASSERT_TRUE(dex_file->IsCompactDexFile());
+    compact_dex_files.push_back(
+        std::unique_ptr<const CompactDexFile>(dex_file.release()->AsCompactDexFile()));
+  }
+  for (const std::unique_ptr<const CompactDexFile>& dex_file : compact_dex_files) {
+    // Test that every code item is in the owned section.
+    const CompactDexFile::Header& header = dex_file->GetHeader();
+    EXPECT_LE(header.OwnedDataBegin(), header.OwnedDataEnd());
+    EXPECT_LE(header.OwnedDataBegin(), header.data_size_);
+    EXPECT_LE(header.OwnedDataEnd(), header.data_size_);
+    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+      class_def.VisitMethods(dex_file.get(), [&](const ClassDataItemIterator& it) {
+        if (it.GetMethodCodeItemOffset() != 0u) {
+          ASSERT_GE(it.GetMethodCodeItemOffset(), header.OwnedDataBegin());
+          ASSERT_LT(it.GetMethodCodeItemOffset(), header.OwnedDataEnd());
+        }
+      });
+    }
+    // Test that the owned sections don't overlap.
+    for (const std::unique_ptr<const CompactDexFile>& other_dex : compact_dex_files) {
+      if (dex_file != other_dex) {
+        ASSERT_TRUE(
+            (dex_file->GetHeader().OwnedDataBegin() >= other_dex->GetHeader().OwnedDataEnd()) ||
+            (dex_file->GetHeader().OwnedDataEnd() <= other_dex->GetHeader().OwnedDataBegin()));
+      }
+    }
   }
 }
 
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index bd76bf1..2b4144c 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -298,6 +298,8 @@
   header.class_defs_off_ = collections.ClassDefsOffset();
   header.data_size_ = header_->DataSize();
   header.data_off_ = header_->DataOffset();
+  header.owned_data_begin_ = owned_data_begin_;
+  header.owned_data_end_ = owned_data_end_;
 
   // Compact dex specific flags.
   header.debug_info_offsets_pos_ = debug_info_offsets_pos_;
@@ -426,6 +428,7 @@
     // Data section.
     data_stream->AlignTo(kDataSectionAlignment);
   }
+  owned_data_begin_ = data_stream->Tell();
 
   // Write code item first to minimize the space required for encoded methods.
   // For cdex, the code items don't depend on the debug info.
@@ -490,6 +493,7 @@
   WriteDebugInfoOffsetTable(data_stream);
 
   data_stream->AlignTo(kDataSectionAlignment);
+  owned_data_end_ = data_stream->Tell();
   if (compute_offsets_) {
     header_->SetDataSize(data_stream->Tell());
     if (header_->DataSize() != 0) {
@@ -497,7 +501,6 @@
       main_stream->AlignTo(kDataSectionAlignment);
       // For now, default to saying the data is right after the main stream.
       header_->SetDataOffset(main_stream->Tell());
-      header_->SetDataOffset(0u);
     } else {
       header_->SetDataOffset(0u);
     }
diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h
index eaf8518..4b142a8 100644
--- a/dexlayout/compact_dex_writer.h
+++ b/dexlayout/compact_dex_writer.h
@@ -169,6 +169,10 @@
   // Base offset of where debug info starts in the dex file.
   uint32_t debug_info_base_ = 0u;
 
+  // Part of the shared data section owned by this file.
+  uint32_t owned_data_begin_ = 0u;
+  uint32_t owned_data_end_ = 0u;
+
   // State for where we are deduping.
   Deduper* code_item_dedupe_ = nullptr;
   Deduper* data_item_dedupe_ = nullptr;
diff --git a/libdexfile/dex/compact_dex_file.h b/libdexfile/dex/compact_dex_file.h
index 78cd768..affc9a2 100644
--- a/libdexfile/dex/compact_dex_file.h
+++ b/libdexfile/dex/compact_dex_file.h
@@ -51,6 +51,16 @@
       return data_size_;
     }
 
+    // Range of the shared data section owned by the dex file. Owned in this context refers to data
+    // for this DEX that was not deduplicated to another DEX.
+    uint32_t OwnedDataBegin() const {
+      return owned_data_begin_;
+    }
+
+    uint32_t OwnedDataEnd() const {
+      return owned_data_end_;
+    }
+
    private:
     uint32_t feature_flags_ = 0u;
 
@@ -63,6 +73,10 @@
     // Base offset of where debug info starts in the dex file.
     uint32_t debug_info_base_ = 0u;
 
+    // Range of the shared data section owned by the dex file.
+    uint32_t owned_data_begin_ = 0u;
+    uint32_t owned_data_end_ = 0u;
+
     friend class CompactDexFile;
     friend class CompactDexWriter;
   };
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index ae0c2f4..d1b3200 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -515,6 +515,18 @@
   return handler_data + offset;
 }
 
+template <typename Visitor>
+inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visitor& visitor) const {
+  const uint8_t* class_data = dex_file->GetClassData(*this);
+  if (class_data != nullptr) {
+    ClassDataItemIterator it(*dex_file, class_data);
+    it.SkipAllFields();
+    for (; it.HasNext(); it.Next()) {
+      visitor(it);
+    }
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 5560cf1..aeb49d2 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -196,6 +196,15 @@
     DISALLOW_COPY_AND_ASSIGN(MethodId);
   };
 
+  // Base code_item, compact dex and standard dex have different code item layouts.
+  struct CodeItem {
+   protected:
+    CodeItem() = default;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(CodeItem);
+  };
+
   // Raw class_def_item.
   struct ClassDef {
     dex::TypeIndex class_idx_;  // index into type_ids_ array for this class
@@ -227,6 +236,9 @@
       }
     }
 
+    template <typename Visitor>
+    void VisitMethods(const DexFile* dex_file, const Visitor& visitor) const;
+
    private:
     DISALLOW_COPY_AND_ASSIGN(ClassDef);
   };
@@ -300,15 +312,6 @@
     DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem);
   };
 
-  // Base code_item, compact dex and standard dex have different code item layouts.
-  struct CodeItem {
-   protected:
-    CodeItem() = default;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(CodeItem);
-  };
-
   // Raw try_item.
   struct TryItem {
     static constexpr size_t kAlignment = sizeof(uint32_t);
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 77e1f2c..326fcbc 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -97,7 +97,8 @@
 
     // The format version of the dex section header and the dex section, containing
     // both the dex code and the quickening data.
-    static constexpr uint8_t kDexSectionVersion[] = { '0', '0', '1', '\0' };
+    // Last update: Add owned section for CompactDex.
+    static constexpr uint8_t kDexSectionVersion[] = { '0', '0', '2', '\0' };
 
     // If the .vdex file has no dex section (hence no dex code nor quickening data),
     // we encode this magic version.