OSDN Git Service

ART: Store classpath information into oat file
authorAndreas Gampe <agampe@google.com>
Thu, 9 Apr 2015 18:15:04 +0000 (11:15 -0700)
committerAndreas Gampe <agampe@google.com>
Tue, 14 Apr 2015 21:16:31 +0000 (21:16 +0000)
Store a "dependency list" of class-path dex-files into the key-value
store of an oat file. The list is made up of dex locations and
corresponding checksums.

Add tests for encoding, decoding and checking the list.

Bug: 19781184
Change-Id: Ie700dd37e6e086db599c95d329ac1f1d2ff0b758

build/Android.gtest.mk
dex2oat/dex2oat.cc
runtime/oat.h
runtime/oat_file.cc
runtime/oat_file.h
runtime/oat_file_test.cc

index d9d09bc..7283710 100644 (file)
@@ -67,6 +67,7 @@ ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
 ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested
+ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
 ART_GTEST_proxy_test_DEX_DEPS := Interfaces
 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
index eda7ec6..7e32b43 100644 (file)
@@ -1237,6 +1237,11 @@ class Dex2Oat FINAL {
       for (auto& class_path_file : class_path_files_) {
         class_path_files.push_back(class_path_file.get());
       }
+
+      // Store the classpath we have right now.
+      key_value_store_->Put(OatHeader::kClassPathKey,
+                            OatFile::EncodeDexFileDependencies(class_path_files));
+
       // Then the dex files we'll compile. Thus we'll resolve the class-path first.
       class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());
 
index de95fef..a31e09a 100644 (file)
@@ -38,6 +38,7 @@ class PACKED(4) OatHeader {
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDex2OatHostKey = "dex2oat-host";
   static constexpr const char* kPicKey = "pic";
+  static constexpr const char* kClassPathKey = "classpath";
 
   static OatHeader* Create(InstructionSet instruction_set,
                            const InstructionSetFeatures* instruction_set_features,
index 81703b1..d3c4b49 100644 (file)
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <cstdlib>
 #include <sstream>
 
 #include "base/bit_vector.h"
@@ -592,4 +593,90 @@ bool OatFile::IsPic() const {
   // TODO: Check against oat_patches. b/18144996
 }
 
+static constexpr char kDexClassPathEncodingSeparator = '*';
+
+std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
+  std::ostringstream out;
+
+  for (const DexFile* dex_file : dex_files) {
+    out << dex_file->GetLocation().c_str();
+    out << kDexClassPathEncodingSeparator;
+    out << dex_file->GetLocationChecksum();
+    out << kDexClassPathEncodingSeparator;
+  }
+
+  return out.str();
+}
+
+bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) {
+  if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
+    // No dependencies.
+    return true;
+  }
+
+  // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
+  //             Split() instead of manual parsing of the combined char*.
+  std::vector<std::string> split;
+  Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
+  if (split.size() % 2 != 0) {
+    // Expected pairs of location and checksum.
+    *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies);
+    return false;
+  }
+
+  for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
+    std::string& location = *it;
+    std::string& checksum = *(it + 1);
+    int64_t converted = strtoll(checksum.c_str(), nullptr, 10);
+    if (converted == 0) {
+      // Conversion error.
+      *msg = StringPrintf("Conversion error for %s", checksum.c_str());
+      return false;
+    }
+
+    uint32_t dex_checksum;
+    std::string error_msg;
+    if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(),
+                             &dex_checksum,
+                             &error_msg)) {
+      if (converted != dex_checksum) {
+        *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u",
+                            location.c_str(), converted, dex_checksum);
+        return false;
+      }
+    } else {
+      // Problem retrieving checksum.
+      // TODO: odex files?
+      *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(),
+                          error_msg.c_str());
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies,
+                                              std::vector<std::string>* locations) {
+  DCHECK(locations != nullptr);
+  if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
+    return true;
+  }
+
+  // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
+  //             Split() instead of manual parsing of the combined char*.
+  std::vector<std::string> split;
+  Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
+  if (split.size() % 2 != 0) {
+    // Expected pairs of location and checksum.
+    return false;
+  }
+
+  for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
+    locations->push_back(*it);
+  }
+
+  return true;
+}
+
 }  // namespace art
index 73a8c8e..a5d5ae8 100644 (file)
@@ -248,6 +248,18 @@ class OatFile FINAL {
   static std::string ResolveRelativeEncodedDexLocation(
       const char* abs_dex_location, const std::string& rel_dex_location);
 
+  // Create a dependency list (dex locations and checksums) for the given dex files.
+  static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files);
+
+  // Check the given dependency list against their dex files - thus the name "Static," this does
+  // not check the class-loader environment, only whether there have been file updates.
+  static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg);
+
+  // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic
+  // locations of multidex files.
+  static bool GetDexLocationsFromDependencies(const char* dex_dependencies,
+                                              std::vector<std::string>* locations);
+
  private:
   static void CheckLocation(const std::string& location);
 
index f2213e9..a88553c 100644 (file)
 
 #include <gtest/gtest.h>
 
+#include "common_runtime_test.h"
+#include "scoped_thread_state_change.h"
+
 namespace art {
 
-TEST(OatFileTest, ResolveRelativeEncodedDexLocation) {
+class OatFileTest : public CommonRuntimeTest {
+};
+
+TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) {
   EXPECT_EQ(std::string("/data/app/foo/base.apk"),
       OatFile::ResolveRelativeEncodedDexLocation(
         nullptr, "/data/app/foo/base.apk"));
@@ -56,4 +62,54 @@ TEST(OatFileTest, ResolveRelativeEncodedDexLocation) {
         "/data/app/foo/base.apk", "o/base.apk"));
 }
 
+static std::vector<const DexFile*> ToConstDexFiles(
+    const std::vector<std::unique_ptr<const DexFile>>& in) {
+  std::vector<const DexFile*> ret;
+  for (auto& d : in) {
+    ret.push_back(d.get());
+  }
+  return ret;
+}
+
+TEST_F(OatFileTest, DexFileDependencies) {
+  std::string error_msg;
+
+  // No dependencies.
+  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg;
+  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg;
+
+  // Ill-formed dependencies.
+  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg));
+  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg));
+  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg));
+
+  // Unsatisfiable dependency.
+  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg));
+
+  // Load some dex files to be able to do a real test.
+  ScopedObjectAccess soa(Thread::Current());
+
+  std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main");
+  std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1);
+  std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1);
+  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg))
+      << error_msg << " " << encoding1;
+  std::vector<std::string> split1;
+  EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1));
+  ASSERT_EQ(split1.size(), 1U);
+  EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation());
+
+  std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex");
+  EXPECT_GT(dex_files2.size(), 1U);
+  std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2);
+  std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2);
+  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg))
+      << error_msg << " " << encoding2;
+  std::vector<std::string> split2;
+  EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2));
+  ASSERT_EQ(split2.size(), 2U);
+  EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation());
+  EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation());
+}
+
 }  // namespace art