Merge "Extend profman to generate profiles with inline caches"
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index d89cdba..9a45379 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -52,10 +52,10 @@
compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
method->GetDexMethodIndex()));
}
- if (compiled_method != nullptr) {
+ // If the code size is 0 it means the method was skipped due to profile guided compilation.
+ if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) {
ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
uint32_t code_size = code.size();
- CHECK_NE(0u, code_size);
ArrayRef<const uint8_t> vmap_table = compiled_method->GetVmapTable();
uint32_t vmap_table_offset = vmap_table.empty() ? 0u
: sizeof(OatQuickMethodHeader) + vmap_table.size();
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
index bfd485d..5360103 100644
--- a/compiler/dex/dex_to_dex_decompiler.cc
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -20,7 +20,7 @@
#include "base/mutex.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
-#include "optimizing/bytecode_utils.h"
+#include "bytecode_utils.h"
namespace art {
namespace optimizer {
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 97954f3..562f97b 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -240,9 +240,8 @@
ProfileCompilationInfo info;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
- std::string key = ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation());
- profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 1);
- profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 2);
+ profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 1);
+ profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 2);
}
return &profile_info_;
}
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 2d084c1..952909c 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -41,7 +41,7 @@
"AAAAdQEAAAAQAAABAAAAjAEAAA==";
static const char kDexFileLayoutInputProfile[] =
- "cHJvADAwMwABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA==";
+ "cHJvADAwNAABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA==";
static const char kDexFileLayoutExpectedOutputDex[] =
"ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH"
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index d395c17..5a758ae 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -16,11 +16,14 @@
#include <gtest/gtest.h>
+#include "art_method-inl.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "exec_utils.h"
-#include "profile_assistant.h"
#include "jit/profile_compilation_info.h"
+#include "mirror/class-inl.h"
+#include "profile_assistant.h"
+#include "scoped_thread_state_change-inl.h"
#include "utils.h"
namespace art {
@@ -95,10 +98,12 @@
return ExecAndReturnCode(argv_str, &error);
}
- bool CreateProfile(std::string class_file_contents, const std::string& filename) {
+ bool CreateProfile(std::string profile_file_contents,
+ const std::string& filename,
+ const std::string& dex_location) {
ScratchFile class_names_file;
File* file = class_names_file.GetFile();
- EXPECT_TRUE(file->WriteFully(class_file_contents.c_str(), class_file_contents.length()));
+ EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
EXPECT_EQ(0, file->Flush());
EXPECT_TRUE(file->ResetOffset());
std::string profman_cmd = GetProfmanCmd();
@@ -106,8 +111,8 @@
argv_str.push_back(profman_cmd);
argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename());
argv_str.push_back("--reference-profile-file=" + filename);
- argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
- argv_str.push_back("--dex-location=classes.dex");
+ argv_str.push_back("--apk=" + dex_location);
+ argv_str.push_back("--dex-location=" + dex_location);
std::string error;
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
return true;
@@ -121,7 +126,7 @@
argv_str.push_back("--dump-classes");
argv_str.push_back("--profile-file=" + filename);
argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
- argv_str.push_back("--dex-location=classes.dex");
+ argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(class_names_file)));
std::string error;
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
@@ -137,11 +142,72 @@
bool CreateAndDump(const std::string& input_file_contents, std::string* output_file_contents) {
ScratchFile profile_file;
- EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename()));
+ EXPECT_TRUE(CreateProfile(input_file_contents,
+ profile_file.GetFilename(),
+ GetLibCoreDexFileNames()[0]));
profile_file.GetFile()->ResetOffset();
EXPECT_TRUE(DumpClasses(profile_file.GetFilename(), output_file_contents));
return true;
}
+
+ mirror::Class* GetClass(jobject class_loader, const std::string& clazz) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_loader(
+ hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
+ return class_linker->FindClass(self, clazz.c_str(), h_loader);
+ }
+
+ ArtMethod* GetVirtualMethod(jobject class_loader,
+ const std::string& clazz,
+ const std::string& name) {
+ mirror::Class* klass = GetClass(class_loader, clazz);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const auto pointer_size = class_linker->GetImagePointerSize();
+ ArtMethod* method = nullptr;
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ for (auto& m : klass->GetVirtualMethods(pointer_size)) {
+ if (name == m.GetName()) {
+ EXPECT_TRUE(method == nullptr);
+ method = &m;
+ }
+ }
+ return method;
+ }
+
+ // Verify that given method has the expected inline caches and nothing else.
+ void AssertInlineCaches(ArtMethod* method,
+ const std::set<mirror::Class*>& expected_clases,
+ const ProfileCompilationInfo& info,
+ bool megamorphic)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+ ASSERT_TRUE(info.GetMethod(method->GetDexFile()->GetLocation(),
+ method->GetDexFile()->GetLocationChecksum(),
+ method->GetDexMethodIndex(),
+ &pmi));
+ ASSERT_EQ(pmi.inline_caches.size(), 1u);
+ ProfileCompilationInfo::DexPcData dex_pc_data = pmi.inline_caches.begin()->second;
+
+ ASSERT_EQ(dex_pc_data.is_megamorphic, megamorphic);
+ ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
+ size_t found = 0;
+ for (mirror::Class* it : expected_clases) {
+ for (const auto& class_ref : dex_pc_data.classes) {
+ ProfileCompilationInfo::DexReference dex_ref =
+ pmi.dex_references[class_ref.dex_profile_index];
+ if (dex_ref.MatchesDex(&(it->GetDexFile())) &&
+ class_ref.type_index == it->GetDexTypeIndex()) {
+ found++;
+ }
+ }
+ }
+
+ ASSERT_EQ(expected_clases.size(), found);
+ }
};
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
@@ -358,25 +424,28 @@
TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
// Class names put here need to be in sorted order.
std::vector<std::string> class_names = {
- "java.lang.Comparable",
- "java.lang.Math",
- "java.lang.Object"
+ "Ljava/lang/Comparable;",
+ "Ljava/lang/Math;",
+ "Ljava/lang/Object;"
};
std::string input_file_contents;
+ std::string expected_contents;
for (std::string& class_name : class_names) {
input_file_contents += class_name + std::string("\n");
+ expected_contents += DescriptorToDot(class_name.c_str()) +
+ std::string("\n");
}
std::string output_file_contents;
ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
- ASSERT_EQ(output_file_contents, input_file_contents);
+ ASSERT_EQ(output_file_contents, expected_contents);
}
TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
// Class names put here need to be in sorted order.
std::vector<std::string> class_names = {
- "doesnt.match.this.one",
- "java.lang.Comparable",
- "java.lang.Object"
+ "Ldoesnt/match/this/one;",
+ "Ljava/lang/Comparable;",
+ "Ljava/lang/Object;"
};
std::string input_file_contents;
for (std::string& class_name : class_names) {
@@ -385,16 +454,17 @@
std::string output_file_contents;
ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
std::string expected_contents =
- class_names[1] + std::string("\n") + class_names[2] + std::string("\n");
+ DescriptorToDot(class_names[1].c_str()) + std::string("\n") +
+ DescriptorToDot(class_names[2].c_str()) + std::string("\n");
ASSERT_EQ(output_file_contents, expected_contents);
}
TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
// Class names put here need to be in sorted order.
std::vector<std::string> class_names = {
- "doesnt.match.this.one",
- "doesnt.match.this.one.either",
- "nor.this.one"
+ "Ldoesnt/match/this/one;",
+ "Ldoesnt/match/this/one/either;",
+ "Lnor/this/one;"
};
std::string input_file_contents;
for (std::string& class_name : class_names) {
@@ -406,4 +476,88 @@
ASSERT_EQ(output_file_contents, expected_contents);
}
+TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
+ // Create the profile content.
+ std::vector<std::string> methods = {
+ "LTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
+ "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
+ "LTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
+ "LTestInline;->noInlineCache(LSuper;)I"
+ };
+ std::string input_file_contents;
+ for (std::string& m : methods) {
+ input_file_contents += m + std::string("\n");
+ }
+
+ // Create the profile and save it to disk.
+ ScratchFile profile_file;
+ ASSERT_TRUE(CreateProfile(input_file_contents,
+ profile_file.GetFilename(),
+ GetTestDexFileName("ProfileTestMultiDex")));
+
+ // Load the profile from disk.
+ ProfileCompilationInfo info;
+ profile_file.GetFile()->ResetOffset();
+ ASSERT_TRUE(info.Load(GetFd(profile_file)));
+
+ // Load the dex files and verify that the profile contains the expected methods info.
+ ScopedObjectAccess soa(Thread::Current());
+ jobject class_loader = LoadDex("ProfileTestMultiDex");
+ ASSERT_NE(class_loader, nullptr);
+
+ mirror::Class* sub_a = GetClass(class_loader, "LSubA;");
+ mirror::Class* sub_b = GetClass(class_loader, "LSubB;");
+ mirror::Class* sub_c = GetClass(class_loader, "LSubC;");
+
+ ASSERT_TRUE(sub_a != nullptr);
+ ASSERT_TRUE(sub_b != nullptr);
+ ASSERT_TRUE(sub_c != nullptr);
+
+ {
+ // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMonomorphic");
+ ASSERT_TRUE(inline_monomorphic != nullptr);
+ std::set<mirror::Class*> expected_monomorphic;
+ expected_monomorphic.insert(sub_a);
+ AssertInlineCaches(inline_monomorphic, expected_monomorphic, info, /*megamorphic*/ false);
+ }
+
+ {
+ // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlinePolymorphic");
+ ASSERT_TRUE(inline_polymorhic != nullptr);
+ std::set<mirror::Class*> expected_polymorphic;
+ expected_polymorphic.insert(sub_a);
+ expected_polymorphic.insert(sub_b);
+ expected_polymorphic.insert(sub_c);
+ AssertInlineCaches(inline_polymorhic, expected_polymorphic, info, /*megamorphic*/ false);
+ }
+
+ {
+ // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMegamorphic");
+ ASSERT_TRUE(inline_megamorphic != nullptr);
+ std::set<mirror::Class*> expected_megamorphic;
+ AssertInlineCaches(inline_megamorphic, expected_megamorphic, info, /*megamorphic*/ true);
+ }
+
+ {
+ // Verify that method noInlineCache has no inline caches in the profile.
+ ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache");
+ ASSERT_TRUE(no_inline_cache != nullptr);
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi_no_inline_cache;
+ ASSERT_TRUE(info.GetMethod(no_inline_cache->GetDexFile()->GetLocation(),
+ no_inline_cache->GetDexFile()->GetLocationChecksum(),
+ no_inline_cache->GetDexMethodIndex(),
+ &pmi_no_inline_cache));
+ ASSERT_TRUE(pmi_no_inline_cache.inline_caches.empty());
+ }
+}
+
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index a42e4f1..a99a0ea 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -36,6 +36,7 @@
#include "base/stringpiece.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
+#include "bytecode_utils.h"
#include "dex_file.h"
#include "jit/profile_compilation_info.h"
#include "runtime.h"
@@ -136,6 +137,14 @@
static constexpr uint16_t kDefaultTestProfileMethodRatio = 5;
static constexpr uint16_t kDefaultTestProfileClassRatio = 5;
+// Separators used when parsing human friendly representation of profiles.
+static const std::string kMethodSep = "->";
+static constexpr char kProfileParsingInlineChacheSep = '+';
+static constexpr char kProfileParsingTypeSep = ',';
+static constexpr char kProfileParsingFirstCharInSignature = '(';
+
+// TODO(calin): This class has grown too much from its initial design. Split the functionality
+// into smaller, more contained pieces.
class ProfMan FINAL {
public:
ProfMan() :
@@ -522,6 +531,180 @@
return output.release();
}
+ // Find class klass_descriptor in the given dex_files and store its reference
+ // in the out parameter class_ref.
+ // Return true if the definition of the class was found in any of the dex_files.
+ bool FindClass(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ const std::string& klass_descriptor,
+ /*out*/ProfileMethodInfo::ProfileClassReference* class_ref) {
+ for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) {
+ const DexFile* dex_file = dex_file_ptr.get();
+ const DexFile::TypeId* type_id = dex_file->FindTypeId(klass_descriptor.c_str());
+ if (type_id == nullptr) {
+ continue;
+ }
+ dex::TypeIndex type_index = dex_file->GetIndexForTypeId(*type_id);
+ if (dex_file->FindClassDef(type_index) == nullptr) {
+ // Class is only referenced in the current dex file but not defined in it.
+ continue;
+ }
+ class_ref->dex_file = dex_file;
+ class_ref->type_index = type_index;
+ return true;
+ }
+ return false;
+ }
+
+ // Find the method specified by method_spec in the class class_ref. The method
+ // must have a single INVOKE_VIRTUAL in its byte code.
+ // Upon success it returns true and stores the method index and the invoke dex pc
+ // in the output parameters.
+ // The format of the method spec is "inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
+ //
+ // TODO(calin): support INVOKE_INTERFACE and the range variants.
+ bool FindMethodWithSingleInvoke(const ProfileMethodInfo::ProfileClassReference& class_ref,
+ const std::string& method_spec,
+ /*out*/uint16_t* method_index,
+ /*out*/uint32_t* dex_pc) {
+ std::vector<std::string> name_and_signature;
+ Split(method_spec, kProfileParsingFirstCharInSignature, &name_and_signature);
+ if (name_and_signature.size() != 2) {
+ LOG(ERROR) << "Invalid method name and signature " << method_spec;
+ }
+ const std::string& name = name_and_signature[0];
+ const std::string& signature = kProfileParsingFirstCharInSignature + name_and_signature[1];
+ const DexFile* dex_file = class_ref.dex_file;
+
+ const DexFile::StringId* name_id = dex_file->FindStringId(name.c_str());
+ if (name_id == nullptr) {
+ LOG(ERROR) << "Could not find name: " << name;
+ return false;
+ }
+ dex::TypeIndex return_type_idx;
+ std::vector<dex::TypeIndex> param_type_idxs;
+ if (!dex_file->CreateTypeList(signature, &return_type_idx, ¶m_type_idxs)) {
+ LOG(ERROR) << "Could not create type list" << signature;
+ return false;
+ }
+ const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type_idx, param_type_idxs);
+ if (proto_id == nullptr) {
+ LOG(ERROR) << "Could not find proto_id: " << name;
+ return false;
+ }
+ const DexFile::MethodId* method_id = dex_file->FindMethodId(
+ dex_file->GetTypeId(class_ref.type_index), *name_id, *proto_id);
+ if (method_id == nullptr) {
+ LOG(ERROR) << "Could not find method_id: " << name;
+ return false;
+ }
+
+ *method_index = dex_file->GetIndexForMethodId(*method_id);
+
+ uint32_t offset = dex_file->FindCodeItemOffset(
+ *dex_file->FindClassDef(class_ref.type_index),
+ *method_index);
+ const DexFile::CodeItem* code_item = dex_file->GetCodeItem(offset);
+
+ bool found_invoke = false;
+ for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) {
+ if (it.CurrentInstruction().Opcode() == Instruction::INVOKE_VIRTUAL) {
+ if (found_invoke) {
+ LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " << name;
+ return false;
+ }
+ found_invoke = true;
+ *dex_pc = it.CurrentDexPc();
+ }
+ }
+ if (!found_invoke) {
+ LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << name;
+ }
+ return found_invoke;
+ }
+
+ // Process a line defining a class or a method and its inline caches.
+ // Upon success return true and add the class or the method info to profile.
+ // The format of the method line is:
+ // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
+ // The method and classes are searched only in the given dex files.
+ bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ const std::string& line,
+ /*out*/ProfileCompilationInfo* profile) {
+ std::string klass;
+ std::string method_str;
+ size_t method_sep_index = line.find(kMethodSep);
+ if (method_sep_index == std::string::npos) {
+ klass = line;
+ } else {
+ klass = line.substr(0, method_sep_index);
+ method_str = line.substr(method_sep_index + kMethodSep.size());
+ }
+
+ ProfileMethodInfo::ProfileClassReference class_ref;
+ if (!FindClass(dex_files, klass, &class_ref)) {
+ LOG(ERROR) << "Could not find class: " << klass;
+ return false;
+ }
+
+ if (method_str.empty()) {
+ // No method to add. Just add the class.
+ std::set<DexCacheResolvedClasses> resolved_class_set;
+ const DexFile* dex_file = class_ref.dex_file;
+ const auto& dex_resolved_classes = resolved_class_set.emplace(
+ dex_file->GetLocation(),
+ dex_file->GetBaseLocation(),
+ dex_file->GetLocationChecksum());
+ dex_resolved_classes.first->AddClass(class_ref.type_index);
+ profile->AddMethodsAndClasses(std::vector<ProfileMethodInfo>(), resolved_class_set);
+ return true;
+ }
+
+ // Process the method.
+ std::string method_spec;
+ std::vector<std::string> inline_cache_elems;
+
+ std::vector<std::string> method_elems;
+ Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
+ if (method_elems.size() == 2) {
+ method_spec = method_elems[0];
+ Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems);
+ } else if (method_elems.size() == 1) {
+ method_spec = method_elems[0];
+ } else {
+ LOG(ERROR) << "Invalid method line: " << line;
+ return false;
+ }
+
+ uint16_t method_index;
+ uint32_t dex_pc;
+ if (!FindMethodWithSingleInvoke(class_ref, method_spec, &method_index, &dex_pc)) {
+ return false;
+ }
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes(inline_cache_elems.size());
+ size_t class_it = 0;
+ for (const std::string ic_class : inline_cache_elems) {
+ if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) {
+ LOG(ERROR) << "Could not find class: " << ic_class;
+ return false;
+ }
+ }
+ std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
+ inline_caches.emplace_back(dex_pc, classes);
+ std::vector<ProfileMethodInfo> pmi;
+ pmi.emplace_back(class_ref.dex_file, method_index, inline_caches);
+
+ profile->AddMethodsAndClasses(pmi, std::set<DexCacheResolvedClasses>());
+ return true;
+ }
+
+ // Creates a profile from a human friendly textual representation.
+ // The expected input format is:
+ // # Classes
+ // Ljava/lang/Comparable;
+ // Ljava/lang/Math;
+ // # Methods with inline caches
+ // LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;
+ // LTestInline;->noInlineCache(LSuper;)I
int CreateProfile() {
// Validate parameters for this command.
if (apk_files_.empty() && apks_fd_.empty()) {
@@ -550,51 +733,22 @@
return -1;
}
}
- // Read the user-specified list of classes (dot notation rather than descriptors).
+ // Read the user-specified list of classes and methods.
std::unique_ptr<std::unordered_set<std::string>>
- user_class_list(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
+ user_lines(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
create_profile_from_file_.c_str(), nullptr)); // No post-processing.
- std::unordered_set<std::string> matched_user_classes;
- // Open the dex files to look up class names.
+
+ // Open the dex files to look up classes and methods.
std::vector<std::unique_ptr<const DexFile>> dex_files;
OpenApkFilesFromLocations(&dex_files);
- // Iterate over the dex files looking for class names in the input stream.
- std::set<DexCacheResolvedClasses> resolved_class_set;
- for (auto& dex_file : dex_files) {
- // Compute the set of classes to be added for this dex file first. This
- // avoids creating an entry in the profile information for dex files that
- // contribute no classes.
- std::unordered_set<dex::TypeIndex> classes_to_be_added;
- for (const auto& klass : *user_class_list) {
- std::string descriptor = DotToDescriptor(klass.c_str());
- const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor.c_str());
- if (type_id == nullptr) {
- continue;
- }
- classes_to_be_added.insert(dex_file->GetIndexForTypeId(*type_id));
- matched_user_classes.insert(klass);
- }
- if (classes_to_be_added.empty()) {
- continue;
- }
- // Insert the DexCacheResolved Classes into the set expected for
- // AddMethodsAndClasses.
- std::set<DexCacheResolvedClasses>::iterator dex_resolved_classes =
- resolved_class_set.emplace(dex_file->GetLocation(),
- dex_file->GetBaseLocation(),
- dex_file->GetLocationChecksum()).first;
- dex_resolved_classes->AddClasses(classes_to_be_added.begin(), classes_to_be_added.end());
- }
- // Warn the user if we didn't find matches for every class.
- for (const auto& klass : *user_class_list) {
- if (matched_user_classes.find(klass) == matched_user_classes.end()) {
- LOG(WARNING) << "requested class '" << klass << "' was not matched in any dex file";
- }
- }
- // Generate the profile data structure.
+
+ // Process the lines one by one and add the successful ones to the profile.
ProfileCompilationInfo info;
- std::vector<ProfileMethodInfo> methods; // No methods for now.
- info.AddMethodsAndClasses(methods, resolved_class_set);
+
+ for (const auto& line : *user_lines) {
+ ProcessLine(dex_files, line, &info);
+ }
+
// Write the profile file.
CHECK(info.Save(fd));
if (close(fd) < 0) {
diff --git a/compiler/optimizing/bytecode_utils.h b/runtime/bytecode_utils.h
similarity index 96%
rename from compiler/optimizing/bytecode_utils.h
rename to runtime/bytecode_utils.h
index 133afa4..fa87b1d 100644
--- a/compiler/optimizing/bytecode_utils.h
+++ b/runtime/bytecode_utils.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_
-#define ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_
+#ifndef ART_RUNTIME_BYTECODE_UTILS_H_
+#define ART_RUNTIME_BYTECODE_UTILS_H_
#include "base/arena_object.h"
#include "dex_file.h"
@@ -177,4 +177,4 @@
} // namespace art
-#endif // ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_
+#endif // ART_RUNTIME_BYTECODE_UTILS_H_
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 3467b60..8c5bb0d 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -37,7 +37,8 @@
namespace art {
const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '3', '\0' }; // inline caches
+// Last profile version: fix the order of dex files in the profile.
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '4', '\0' };
static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
@@ -222,15 +223,23 @@
DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
+ // Make sure we write the dex files in order of their profile index. This
+ // avoids writing the index in the output file and simplifies the parsing logic.
+ std::vector<const std::string*> ordered_info_location(info_.size());
+ std::vector<const DexFileData*> ordered_info_data(info_.size());
for (const auto& it : info_) {
+ ordered_info_location[it.second.profile_index] = &(it.first);
+ ordered_info_data[it.second.profile_index] = &(it.second);
+ }
+ for (size_t i = 0; i < info_.size(); i++) {
if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
return false;
}
buffer.clear();
}
- const std::string& dex_location = it.first;
- const DexFileData& dex_data = it.second;
+ const std::string& dex_location = *ordered_info_location[i];
+ const DexFileData& dex_data = *ordered_info_data[i];
// Note that we allow dex files without any methods or classes, so that
// inline caches can refer valid dex files.
@@ -797,10 +806,13 @@
SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
+ uint32_t other_checksum = other_it.second.checksum;
const DexFileData& other_dex_data = other_it.second;
- auto info_it = info_.FindOrAdd(other_dex_location, DexFileData(other_dex_data.checksum, 0));
- const DexFileData& dex_data = info_it->second;
- dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data.profile_index);
+ const DexFileData* dex_data = GetOrAddDexFileData(other_dex_location, other_checksum);
+ if (dex_data == nullptr) {
+ return false; // Could happen if we exceed the number of allowed dex files.
+ }
+ dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data->profile_index);
}
// Merge the actual profile data.
@@ -949,10 +961,17 @@
os << "ProfileInfo:";
const std::string kFirstDexFileKeySubstitute = ":classes.dex";
+ // Write the entries in profile index order.
+ std::vector<const std::string*> ordered_info_location(info_.size());
+ std::vector<const DexFileData*> ordered_info_data(info_.size());
for (const auto& it : info_) {
+ ordered_info_location[it.second.profile_index] = &(it.first);
+ ordered_info_data[it.second.profile_index] = &(it.second);
+ }
+ for (size_t profile_index = 0; profile_index < info_.size(); profile_index++) {
os << "\n";
- const std::string& location = it.first;
- const DexFileData& dex_data = it.second;
+ const std::string& location = *ordered_info_location[profile_index];
+ const DexFileData& dex_data = *ordered_info_data[profile_index];
if (print_full_dex_location) {
os << location;
} else {
@@ -960,6 +979,7 @@
std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
}
+ os << " [index=" << static_cast<uint32_t>(dex_data.profile_index) << "]";
const DexFile* dex_file = nullptr;
if (dex_files != nullptr) {
for (size_t i = 0; i < dex_files->size(); i++) {
@@ -1022,7 +1042,8 @@
const DexFile* dex_file = nullptr;
if (dex_files != nullptr) {
for (size_t i = 0; i < dex_files->size(); i++) {
- if (location == (*dex_files)[i]->GetLocation()) {
+ if (location == GetProfileDexFileKey((*dex_files)[i]->GetLocation()) &&
+ dex_data.checksum == (*dex_files)[i]->GetLocationChecksum()) {
dex_file = (*dex_files)[i];
}
}
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index cf556bf..f089dff 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -36,11 +36,12 @@
*/
struct ProfileMethodInfo {
struct ProfileClassReference {
+ ProfileClassReference() : dex_file(nullptr) {}
ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index)
: dex_file(dex), type_index(index) {}
const DexFile* dex_file;
- const dex::TypeIndex type_index;
+ dex::TypeIndex type_index;
};
struct ProfileInlineCache {
@@ -91,6 +92,11 @@
return dex_checksum == other.dex_checksum && dex_location == other.dex_location;
}
+ bool MatchesDex(const DexFile* dex_file) const {
+ return dex_checksum == dex_file->GetLocationChecksum() &&
+ dex_location == GetProfileDexFileKey(dex_file->GetLocation());
+ }
+
std::string dex_location;
uint32_t dex_checksum;
};
diff --git a/test/ProfileTestMultiDex/Main.java b/test/ProfileTestMultiDex/Main.java
index 41532ea..73fbb00 100644
--- a/test/ProfileTestMultiDex/Main.java
+++ b/test/ProfileTestMultiDex/Main.java
@@ -25,3 +25,41 @@
return "C";
}
}
+
+class TestInline {
+ public int inlineMonomorphic(Super s) {
+ return s.getValue();
+ }
+
+ public int inlinePolymorphic(Super s) {
+ return s.getValue();
+ }
+
+ public int inlineMegamorphic(Super s) {
+ return s.getValue();
+ }
+
+ public int noInlineCache(Super s) {
+ return s.getValue();
+ }
+}
+
+abstract class Super {
+ abstract int getValue();
+}
+
+class SubA extends Super {
+ int getValue() { return 42; }
+}
+
+class SubB extends Super {
+ int getValue() { return 38; };
+}
+
+class SubD extends Super {
+ int getValue() { return 20; };
+}
+
+class SubE extends Super {
+ int getValue() { return 16; };
+}
diff --git a/test/ProfileTestMultiDex/Second.java b/test/ProfileTestMultiDex/Second.java
index 4ac5abc..4b3c7a4 100644
--- a/test/ProfileTestMultiDex/Second.java
+++ b/test/ProfileTestMultiDex/Second.java
@@ -25,3 +25,8 @@
return "Z";
}
}
+
+class SubC extends Super {
+ int getValue() { return 24; }
+}
+
diff --git a/test/ProfileTestMultiDex/main.jpp b/test/ProfileTestMultiDex/main.jpp
index f2e3b4e..5e55e96 100644
--- a/test/ProfileTestMultiDex/main.jpp
+++ b/test/ProfileTestMultiDex/main.jpp
@@ -1,3 +1,21 @@
-main:
+Main:
@@com.android.jack.annotations.ForceInMainDex
- class Second
+ class Main
+TestInqline:
+ @@com.android.jack.annotations.ForceInMainDex
+ class TestInline
+Super:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Super
+SubA:
+ @@com.android.jack.annotations.ForceInMainDex
+ class SubA
+SubB:
+ @@com.android.jack.annotations.ForceInMainDex
+ class SubB
+SubD:
+ @@com.android.jack.annotations.ForceInMainDex
+ class SubD
+SubE:
+ @@com.android.jack.annotations.ForceInMainDex
+ class SubE
diff --git a/test/ProfileTestMultiDex/main.list b/test/ProfileTestMultiDex/main.list
index 44ba78e..ec131f0 100644
--- a/test/ProfileTestMultiDex/main.list
+++ b/test/ProfileTestMultiDex/main.list
@@ -1 +1,7 @@
Main.class
+TestInline.class
+Super.class
+SubA.class
+SubB.class
+SubD.class
+SubE.class