summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gampe <agampe@google.com>2015-04-09 11:15:04 -0700
committerAndreas Gampe <agampe@google.com>2015-04-14 21:16:31 +0000
commit7848da48a0a4241dedc1cc83ac4931e61575eb92 (patch)
tree58a789b6b21893135a8770496175ccc7259d7ea6
parent6c7bd25b2b2a07740bcd838c2f4024e67be83fd1 (diff)
downloadart-7848da48a0a4241dedc1cc83ac4931e61575eb92.zip
art-7848da48a0a4241dedc1cc83ac4931e61575eb92.tar.gz
art-7848da48a0a4241dedc1cc83ac4931e61575eb92.tar.bz2
ART: Store classpath information into oat file
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
-rw-r--r--build/Android.gtest.mk1
-rw-r--r--dex2oat/dex2oat.cc5
-rw-r--r--runtime/oat.h1
-rw-r--r--runtime/oat_file.cc87
-rw-r--r--runtime/oat_file.h12
-rw-r--r--runtime/oat_file_test.cc58
6 files changed, 163 insertions, 1 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index d9d09bc..7283710 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -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
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index eda7ec6..7e32b43 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -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());
diff --git a/runtime/oat.h b/runtime/oat.h
index de95fef..a31e09a 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -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,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 81703b1..d3c4b49 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -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
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 73a8c8e..a5d5ae8 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -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);
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index f2213e9..a88553c 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -20,9 +20,15 @@
#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