summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2021-02-03 18:19:03 -0800
committer Treehugger Robot <treehugger-gerrit@google.com> 2021-02-19 20:32:08 +0000
commita2f1319a8941ae52c98594824b919c08eeb0ecd1 (patch)
treedf07248cd6701bf764c01ed1322b355c16e159bf
parent642c0f0e79ce35f11e0af5625618cb126ad40ffa (diff)
Add text-profile support for multiple ICs
Text profile inline-cache support was limited to methods with only a single invoke. This extends support so it is instead based on the receiver type and supports an arbitrary number of invokes. It does assume that all invokes of the same receivers should have the same ICs. This enables us to create text profiles that can survive most common edits to the underlying java language files. IC lines are of the following format <<CLASS_GROUP>> := {CLASS}(,{CLASS})* <<IC_GROUP>> := \[{CLASS}{CLASS_GROUP} <<IC_LINE>> := {PROFILE_FLAGS}{CLASS}->{METHOD}\+{IC_GROUP}* This means a typical line might look like: ``` HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;[LSecret;LSubB;,LSubC; ``` Note that old style single-invoke IC lines are still supported as well and their format has not changed. Updated --dump-classes-and-methods to dump ICs using this format. Note that it will combine ICs for different pcs with the same target so it is possible to construct a profile where the 'profile -> dump -> profile' operation is not idempotent. Any profile coming from a text-dump will be idempotent under this transform. Test: ./test.py --host Bug: 168941430 Change-Id: I69ba3b312caa7ca454487aaeb49862e393de3a4a
-rw-r--r--libdexfile/dex/dex_file.h3
-rw-r--r--libdexfile/dex/dex_file_reference.h16
-rw-r--r--libprofile/profile/profile_compilation_info.cc8
-rw-r--r--libprofile/profile/profile_compilation_info.h10
-rw-r--r--profman/profile_assistant_test.cc322
-rw-r--r--profman/profman.cc338
-rw-r--r--test/ProfileTestMultiDex/Main.java34
-rw-r--r--test/ProfileTestMultiDex/Second.java1
-rw-r--r--test/ProfileTestMultiDex/main.jpp3
-rw-r--r--test/ProfileTestMultiDex/main.list1
10 files changed, 663 insertions, 73 deletions
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index a28856af5d..d97327c12b 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -271,6 +271,9 @@ class DexFile {
const dex::StringId* FindStringId(const char* string) const;
const dex::TypeId* FindTypeId(const char* string) const;
+ const dex::TypeId* FindTypeId(std::string_view string) const {
+ return FindTypeId(std::string(string).c_str());
+ }
// Returns the number of type identifiers in the .dex file.
uint32_t NumTypeIds() const {
diff --git a/libdexfile/dex/dex_file_reference.h b/libdexfile/dex/dex_file_reference.h
index 1c6ba13968..dcc0398811 100644
--- a/libdexfile/dex/dex_file_reference.h
+++ b/libdexfile/dex/dex_file_reference.h
@@ -17,7 +17,9 @@
#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
#define ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
+#include <cstddef>
#include <cstdint>
+#include <utility>
namespace art {
@@ -46,7 +48,21 @@ inline bool operator<(const DexFileReference& a, const DexFileReference& b) {
inline bool operator==(const DexFileReference& a, const DexFileReference& b) {
return a.dex_file == b.dex_file && a.index == b.index;
}
+inline bool operator!=(const DexFileReference& a, const DexFileReference& b) {
+ return !(a == b);
+}
} // namespace art
+namespace std {
+// Hash implementation used for std::set/std::map. Simply xor the hash of the dex-file and index
+template <>
+struct hash<art::DexFileReference> {
+ size_t operator()(const art::DexFileReference& ref) const {
+ return hash<decltype(ref.dex_file)>()(ref.dex_file) ^
+ hash<decltype(ref.index)>()(ref.index);
+ }
+};
+} // namespace std
+
#endif // ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index cbf86b8c14..ddebd6df5c 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -735,14 +735,18 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi,
FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
continue;
}
+ if (cache.is_megamorphic) {
+ FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMegamorphic();
+ continue;
+ }
for (const TypeReference& class_ref : cache.classes) {
DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file, annotation);
if (class_dex_data == nullptr) { // checksum mismatch
return false;
}
DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
- if (dex_pc_data->is_missing_types) {
- // Don't bother adding classes if we are missing types.
+ if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
+ // Don't bother adding classes if we are missing types or already megamorphic.
break;
}
dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.TypeIndex());
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 0b0c4236ba..54a62c5a72 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -44,12 +44,18 @@ struct ProfileMethodInfo {
struct ProfileInlineCache {
ProfileInlineCache(uint32_t pc,
bool missing_types,
- const std::vector<TypeReference>& profile_classes)
- : dex_pc(pc), is_missing_types(missing_types), classes(profile_classes) {}
+ const std::vector<TypeReference>& profile_classes,
+ // Only used by profman for creating profiles from text
+ bool megamorphic = false)
+ : dex_pc(pc),
+ is_missing_types(missing_types),
+ classes(profile_classes),
+ is_megamorphic(megamorphic) {}
const uint32_t dex_pc;
const bool is_missing_types;
const std::vector<TypeReference> classes;
+ const bool is_megamorphic;
};
explicit ProfileMethodInfo(MethodReference reference) : ref(reference) {}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 1fca494593..f9c1abb8d0 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -19,10 +19,13 @@
#include "android-base/file.h"
#include "android-base/strings.h"
#include "art_method-inl.h"
+#include "base/globals.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "common_runtime_test.h"
#include "dex/descriptors_names.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_instruction_iterator.h"
#include "dex/type_reference.h"
#include "exec_utils.h"
#include "linear_alloc.h"
@@ -93,9 +96,10 @@ class ProfileAssistantTest : public CommonRuntimeTest {
for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
// reverse_dex_write_order controls the order in which the dex files will be added to
// the profile and thus written to disk.
- std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches(dex_file1 , dex_file2, dex3);
- Hotness::Flag flags = static_cast<Hotness::Flag>(
- Hotness::kFlagHot | Hotness::kFlagPostStartup);
+ std::vector<ProfileInlineCache> inline_caches =
+ GetTestInlineCaches(dex_file1, dex_file2, dex3);
+ Hotness::Flag flags =
+ static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagPostStartup);
if (reverse_dex_write_order) {
ASSERT_TRUE(AddMethod(info, dex_file2, i, inline_caches, flags));
ASSERT_TRUE(AddMethod(info, dex_file1, i, inline_caches, flags));
@@ -245,24 +249,25 @@ class ProfileAssistantTest : public CommonRuntimeTest {
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);
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
return true;
}
bool RunProfman(const std::string& filename,
std::vector<std::string>& extra_args,
- std::string* output) {
+ std::string* output,
+ std::string_view target_apk) {
ScratchFile output_file;
std::string profman_cmd = GetProfmanCmd();
std::vector<std::string> argv_str;
argv_str.push_back(profman_cmd);
argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
argv_str.push_back("--profile-file=" + filename);
- argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
- argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
+ argv_str.push_back(std::string("--apk=").append(target_apk));
+ argv_str.push_back(std::string("--dex-location=").append(target_apk));
argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
File* file = output_file.GetFile();
EXPECT_EQ(0, file->Flush());
EXPECT_TRUE(file->ResetOffset());
@@ -273,26 +278,30 @@ class ProfileAssistantTest : public CommonRuntimeTest {
return true;
}
- bool DumpClassesAndMethods(const std::string& filename, std::string* file_contents) {
+ bool DumpClassesAndMethods(const std::string& filename,
+ std::string* file_contents,
+ std::optional<const std::string_view> target = std::nullopt) {
std::vector<std::string> extra_args;
extra_args.push_back("--dump-classes-and-methods");
- return RunProfman(filename, extra_args, file_contents);
+ return RunProfman(
+ filename, extra_args, file_contents, target.value_or(GetLibCoreDexFileNames()[0]));
}
bool DumpOnly(const std::string& filename, std::string* file_contents) {
std::vector<std::string> extra_args;
extra_args.push_back("--dump-only");
- return RunProfman(filename, extra_args, file_contents);
+ return RunProfman(filename, extra_args, file_contents, GetLibCoreDexFileNames()[0]);
}
bool CreateAndDump(const std::string& input_file_contents,
- std::string* output_file_contents) {
+ std::string* output_file_contents,
+ std::optional<const std::string> target = std::nullopt) {
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetLibCoreDexFileNames()[0]));
+ target.value_or(GetLibCoreDexFileNames()[0])));
profile_file.GetFile()->ResetOffset();
- EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
+ EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents, target));
return true;
}
@@ -328,7 +337,52 @@ class ProfileAssistantTest : public CommonRuntimeTest {
return TypeReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
}
- // Verify that given method has the expected inline caches and nothing else.
+ // Find the first dex-pc in the given method after 'start_pc' (if given) which
+ // contains a call to any method of 'klass'. If 'start_pc' is not given we
+ // will search from the first dex-pc.
+ uint16_t GetDexPcOfCallTo(ArtMethod* method,
+ Handle<mirror::Class> klass,
+ std::optional<uint32_t> start_pc = std::nullopt)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const DexFile* dex_file = method->GetDexFile();
+ for (const DexInstructionPcPair& inst :
+ CodeItemInstructionAccessor(*dex_file, method->GetCodeItem())) {
+ if (start_pc && inst.DexPc() <= *start_pc) {
+ continue;
+ } else if (inst->IsInvoke()) {
+ const dex::MethodId& method_id = dex_file->GetMethodId(inst->VRegB());
+ std::string_view desc(
+ dex_file->GetTypeDescriptor(dex_file->GetTypeId(method_id.class_idx_)));
+ std::string scratch;
+ if (desc == klass->GetDescriptor(&scratch)) {
+ return inst.DexPc();
+ }
+ }
+ }
+ EXPECT_TRUE(false) << "Unable to find dex-pc in " << method->PrettyMethod() << " for call to "
+ << klass->PrettyClass()
+ << " after dexpc: " << (start_pc ? static_cast<int64_t>(*start_pc) : -1);
+ return -1;
+ }
+
+ void AssertInlineCaches(ArtMethod* method,
+ uint16_t dex_pc,
+ const TypeReferenceSet& expected_clases,
+ const ProfileCompilationInfo& info,
+ bool is_megamorphic,
+ bool is_missing_types)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
+ info.GetHotMethodInfo(MethodReference(
+ method->GetDexFile(), method->GetDexMethodIndex()));
+ ASSERT_TRUE(pmi != nullptr);
+ ASSERT_TRUE(pmi->inline_caches->find(dex_pc) != pmi->inline_caches->end());
+ AssertInlineCaches(expected_clases,
+ pmi.get(),
+ pmi->inline_caches->find(dex_pc)->second,
+ is_megamorphic,
+ is_missing_types);
+ }
void AssertInlineCaches(ArtMethod* method,
const TypeReferenceSet& expected_clases,
const ProfileCompilationInfo& info,
@@ -340,8 +394,19 @@ class ProfileAssistantTest : public CommonRuntimeTest {
method->GetDexFile(), method->GetDexMethodIndex()));
ASSERT_TRUE(pmi != nullptr);
ASSERT_EQ(pmi->inline_caches->size(), 1u);
- const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second;
+ AssertInlineCaches(expected_clases,
+ pmi.get(),
+ pmi->inline_caches->begin()->second,
+ is_megamorphic,
+ is_missing_types);
+ }
+ void AssertInlineCaches(const TypeReferenceSet& expected_clases,
+ ProfileCompilationInfo::OfflineProfileMethodInfo* pmi,
+ const ProfileCompilationInfo::DexPcData& dex_pc_data,
+ bool is_megamorphic,
+ bool is_missing_types)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
@@ -669,8 +734,12 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
ProfileCompilationInfo info1;
SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo reference_info;
- SetupProfile(
- dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, reference_profile, &reference_info);
+ SetupProfile(dex1_checksum_missmatch,
+ dex2,
+ kNumberOfMethodsToEnableCompilation,
+ 0,
+ reference_profile,
+ &reference_info);
// We should not advise compilation.
ASSERT_TRUE(profile1.GetFile()->ResetOffset());
@@ -1030,23 +1099,81 @@ TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
ASSERT_EQ(output_file_contents, expected_contents);
}
+// Test that we can dump profiles in a way they can be re-constituted.
+// Test goes 'txt -> prof -> txt -> prof' and then compares the two profs.
+TEST_F(ProfileAssistantTest, TestProfileRoundTrip) {
+ // Create the profile content.
+ std::vector<std::string_view> methods = {
+ "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
+ "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
+ "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
+ "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
+ "HLTestInline;->noInlineCache(LSuper;)I",
+ "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+[LSuper;LSubA;[LSecret;LSubB;",
+ "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;[LSecret;LSubB;,LSubC;",
+ "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;[LSecret;megamorphic_types",
+ "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+[LSuper;missing_types[LSecret;missing_types",
+ "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;[LSecret;LSubB;,LSubC;",
+ "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
+ };
+ std::ostringstream input_file_contents;
+ for (const std::string_view& m : methods) {
+ input_file_contents << m << "\n";
+ }
+
+ // Create the profile and save it to disk.
+ ScratchFile profile_file;
+ ASSERT_TRUE(CreateProfile(input_file_contents.str(),
+ profile_file.GetFilename(),
+ GetTestDexFileName("ProfileTestMultiDex")));
+ profile_file.GetFile()->ResetOffset();
+
+ // Dump the file back into text.
+ std::string text_two;
+ ASSERT_TRUE(DumpClassesAndMethods(
+ profile_file.GetFilename(), &text_two, GetTestDexFileName("ProfileTestMultiDex")));
+
+ // Create another profile and save it to the disk as well.
+ ScratchFile profile_two;
+ ASSERT_TRUE(CreateProfile(
+ text_two, profile_two.GetFilename(), GetTestDexFileName("ProfileTestMultiDex")));
+ profile_two.GetFile()->ResetOffset();
+
+ // These two profiles should be bit-identical.
+ // TODO We could compare the 'text_two' to the methods but since the order is
+ // arbitrary for many parts and there are multiple 'correct' dumps we'd need
+ // to basically parse everything and this is simply easier.
+ std::string error;
+ std::vector<std::string> args { kIsTargetBuild ? "/system/bin/cmp" : "/usr/bin/cmp",
+ "-s",
+ profile_file.GetFilename(),
+ profile_two.GetFilename() };
+ ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error << " from " << text_two;
+}
+
TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
// Create the profile content.
- std::vector<std::string> methods = {
+ std::vector<std::string_view> methods = {
"HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
"HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
"HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
"HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
- "HLTestInline;->noInlineCache(LSuper;)I"
+ "HLTestInline;->noInlineCache(LSuper;)I",
+ "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+[LSuper;LSubA;[LSecret;LSubB;",
+ "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;[LSecret;LSubB;,LSubC;",
+ "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;[LSecret;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
+ "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+[LSuper;missing_types[LSecret;missing_types",
+ "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+[LSuper;LSubA;,LSubB;,LSubC;[LSecret;LSubB;,LSubC;",
+ "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
};
- std::string input_file_contents;
- for (std::string& m : methods) {
- input_file_contents += m + std::string("\n");
+ std::ostringstream input_file_contents;
+ for (const std::string_view& m : methods) {
+ input_file_contents << m << "\n";
}
// Create the profile and save it to disk.
ScratchFile profile_file;
- ASSERT_TRUE(CreateProfile(input_file_contents,
+ ASSERT_TRUE(CreateProfile(input_file_contents.str(),
profile_file.GetFilename(),
GetTestDexFileName("ProfileTestMultiDex")));
@@ -1060,11 +1187,15 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
jobject class_loader = LoadDex("ProfileTestMultiDex");
ASSERT_NE(class_loader, nullptr);
- StackHandleScope<3> hs(soa.Self());
+ StackHandleScope<5> hs(soa.Self());
+ Handle<mirror::Class> super_klass = hs.NewHandle(GetClass(soa, class_loader, "LSuper;"));
+ Handle<mirror::Class> secret_klass = hs.NewHandle(GetClass(soa, class_loader, "LSecret;"));
Handle<mirror::Class> sub_a = hs.NewHandle(GetClass(soa, class_loader, "LSubA;"));
Handle<mirror::Class> sub_b = hs.NewHandle(GetClass(soa, class_loader, "LSubB;"));
Handle<mirror::Class> sub_c = hs.NewHandle(GetClass(soa, class_loader, "LSubC;"));
+ ASSERT_TRUE(super_klass != nullptr);
+ ASSERT_TRUE(secret_klass != nullptr);
ASSERT_TRUE(sub_a != nullptr);
ASSERT_TRUE(sub_b != nullptr);
ASSERT_TRUE(sub_c != nullptr);
@@ -1139,6 +1270,147 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
ASSERT_TRUE(pmi_no_inline_cache != nullptr);
ASSERT_TRUE(pmi_no_inline_cache->inline_caches->empty());
}
+
+ {
+ // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMultiMonomorphic");
+ ASSERT_TRUE(inline_monomorphic != nullptr);
+ TypeReferenceSet expected_monomorphic_super;
+ TypeReferenceSet expected_monomorphic_secret;
+ expected_monomorphic_super.insert(MakeTypeReference(sub_a.Get()));
+ expected_monomorphic_secret.insert(MakeTypeReference(sub_b.Get()));
+ AssertInlineCaches(inline_monomorphic,
+ GetDexPcOfCallTo(inline_monomorphic, super_klass),
+ expected_monomorphic_super,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ AssertInlineCaches(inline_monomorphic,
+ GetDexPcOfCallTo(inline_monomorphic, secret_klass),
+ expected_monomorphic_secret,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ }
+
+ {
+ // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMultiPolymorphic");
+ ASSERT_TRUE(inline_polymorhic != nullptr);
+ TypeReferenceSet expected_polymorphic_super;
+ expected_polymorphic_super.insert(MakeTypeReference(sub_a.Get()));
+ expected_polymorphic_super.insert(MakeTypeReference(sub_b.Get()));
+ expected_polymorphic_super.insert(MakeTypeReference(sub_c.Get()));
+ TypeReferenceSet expected_polymorphic_secret;
+ expected_polymorphic_secret.insert(MakeTypeReference(sub_b.Get()));
+ expected_polymorphic_secret.insert(MakeTypeReference(sub_c.Get()));
+ AssertInlineCaches(inline_polymorhic,
+ GetDexPcOfCallTo(inline_polymorhic, super_klass),
+ expected_polymorphic_super,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ AssertInlineCaches(inline_polymorhic,
+ GetDexPcOfCallTo(inline_polymorhic, secret_klass),
+ expected_polymorphic_secret,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ }
+
+ {
+ // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineTriplePolymorphic");
+ ASSERT_TRUE(inline_polymorhic != nullptr);
+ TypeReferenceSet expected_polymorphic_super;
+ expected_polymorphic_super.insert(MakeTypeReference(sub_a.Get()));
+ expected_polymorphic_super.insert(MakeTypeReference(sub_b.Get()));
+ expected_polymorphic_super.insert(MakeTypeReference(sub_c.Get()));
+ TypeReferenceSet expected_polymorphic_secret;
+ expected_polymorphic_secret.insert(MakeTypeReference(sub_b.Get()));
+ expected_polymorphic_secret.insert(MakeTypeReference(sub_c.Get()));
+ AssertInlineCaches(inline_polymorhic,
+ GetDexPcOfCallTo(inline_polymorhic, super_klass),
+ expected_polymorphic_super,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ uint16_t first_call = GetDexPcOfCallTo(inline_polymorhic, secret_klass);
+ AssertInlineCaches(inline_polymorhic,
+ first_call,
+ expected_polymorphic_secret,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ uint16_t second_call = GetDexPcOfCallTo(inline_polymorhic, secret_klass, first_call);
+ ASSERT_LT(first_call, second_call);
+ AssertInlineCaches(inline_polymorhic,
+ second_call,
+ expected_polymorphic_secret,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/false);
+ }
+
+ {
+ // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMultiMegamorphic");
+ ASSERT_TRUE(inline_megamorphic != nullptr);
+ TypeReferenceSet expected_megamorphic;
+ AssertInlineCaches(inline_megamorphic,
+ GetDexPcOfCallTo(inline_megamorphic, super_klass),
+ expected_megamorphic,
+ info,
+ /*is_megamorphic=*/true,
+ /*is_missing_types=*/false);
+ AssertInlineCaches(inline_megamorphic,
+ GetDexPcOfCallTo(inline_megamorphic, secret_klass),
+ expected_megamorphic,
+ info,
+ /*is_megamorphic=*/true,
+ /*is_missing_types=*/false);
+ }
+
+ {
+ // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMultiMissingTypes");
+ ASSERT_TRUE(inline_missing_types != nullptr);
+ TypeReferenceSet expected_missing_Types;
+ AssertInlineCaches(inline_missing_types,
+ GetDexPcOfCallTo(inline_missing_types, super_klass),
+ expected_missing_Types,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/true);
+ AssertInlineCaches(inline_missing_types,
+ GetDexPcOfCallTo(inline_missing_types, secret_klass),
+ expected_missing_Types,
+ info,
+ /*is_megamorphic=*/false,
+ /*is_missing_types=*/true);
+ }
+
+ {
+ // Verify that method noInlineCacheMulti has no inline caches in the profile.
+ ArtMethod* no_inline_cache =
+ GetVirtualMethod(class_loader, "LTestInline;", "noInlineCacheMulti");
+ ASSERT_TRUE(no_inline_cache != nullptr);
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi_no_inline_cache =
+ info.GetHotMethodInfo(
+ MethodReference(no_inline_cache->GetDexFile(), no_inline_cache->GetDexMethodIndex()));
+ ASSERT_TRUE(pmi_no_inline_cache != nullptr);
+ ASSERT_TRUE(pmi_no_inline_cache->inline_caches->empty());
+ }
}
TEST_F(ProfileAssistantTest, MergeProfilesWithDifferentDexOrder) {
diff --git a/profman/profman.cc b/profman/profman.cc
index 4eae557343..781843a5dd 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -21,8 +21,10 @@
#include <sys/param.h>
#include <unistd.h>
+#include <cstdint>
#include <fstream>
#include <iostream>
+#include <ostream>
#include <set>
#include <string>
#include <string_view>
@@ -33,6 +35,7 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/array_ref.h"
#include "base/dumpable.h"
#include "base/logging.h" // For InitLogging.
#include "base/mem_map.h"
@@ -47,10 +50,13 @@
#include "dex/art_dex_file_loader.h"
#include "dex/bytecode_utils.h"
#include "dex/class_accessor-inl.h"
+#include "dex/class_reference.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
+#include "dex/dex_file_structs.h"
#include "dex/dex_file_types.h"
+#include "dex/method_reference.h"
#include "dex/type_reference.h"
#include "profile/profile_boot_info.h"
#include "profile/profile_compilation_info.h"
@@ -134,8 +140,10 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --generate-test-profile-seed=<number>: seed for random number generator used when");
UsageError(" generating random test profiles. Defaults to using NanoTime.");
UsageError("");
- UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes and");
- UsageError(" methods.");
+ UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes,");
+ UsageError(" methods and inline caches.");
+ UsageError(" --generate-boot-android-profile: Generate a 012 version profile based on input");
+ UsageError(" profile. Requires --create-profile-from");
UsageError("");
UsageError(" --dex-location=<string>: location string to use with corresponding");
UsageError(" apk-fd to find dex files");
@@ -194,12 +202,14 @@ static constexpr uint16_t kDefaultTestProfileClassPercentage = 5;
// Separators used when parsing human friendly representation of profiles.
static const std::string kMethodSep = "->"; // NOLINT [runtime/string] [4]
static const std::string kMissingTypesMarker = "missing_types"; // NOLINT [runtime/string] [4]
+static const std::string kMegamorphicTypesMarker = "megamorphic_types"; // NOLINT [runtime/string] [4]
static const std::string kInvalidClassDescriptor = "invalid_class"; // NOLINT [runtime/string] [4]
static const std::string kInvalidMethod = "invalid_method"; // NOLINT [runtime/string] [4]
static const std::string kClassAllMethods = "*"; // NOLINT [runtime/string] [4]
static constexpr char kAnnotationStart = '{';
static constexpr char kAnnotationEnd = '}';
static constexpr char kProfileParsingInlineChacheSep = '+';
+static constexpr char kProfileParsingInlineChacheTargetSep = '[';
static constexpr char kProfileParsingTypeSep = ',';
static constexpr char kProfileParsingFirstCharInSignature = '(';
static constexpr char kMethodFlagStringHot = 'H';
@@ -275,6 +285,7 @@ class ProfMan final {
dump_only_(false),
dump_classes_and_methods_(false),
generate_boot_image_profile_(false),
+ generate_boot_android_profile_(false),
generate_boot_profile_(false),
dump_output_to_fd_(File::kInvalidFd),
test_profile_num_dex_(kDefaultTestProfileNumDex),
@@ -323,6 +334,8 @@ class ProfMan final {
generate_boot_profile_ = true;
} else if (option == "--generate-boot-image-profile") {
generate_boot_image_profile_ = true;
+ } else if (option == "--generate-boot-android-profile") {
+ generate_boot_android_profile_ = true;
} else if (StartsWith(option, "--method-threshold=")) {
ParseUintOption(raw_option,
"--method-threshold=",
@@ -745,6 +758,84 @@ class ProfMan final {
return dump_only_;
}
+ // Creates the inline-cache portion of a text-profile line. If there is no
+ // inline-caches this will be and empty string. Otherwise it will be '@'
+ // followed by an IC description matching the format described by ProcessLine
+ // below. Note that this will collapse all ICs with the same receiver type.
+ std::string GetInlineCacheLine(const ProfileCompilationInfo& profile_info,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ const dex::MethodId& id,
+ const DexFile* dex_file,
+ uint16_t dex_method_idx) {
+ auto method_info = profile_info.GetHotMethodInfo(MethodReference(dex_file, dex_method_idx));
+ if (method_info == nullptr || method_info->inline_caches->empty()) {
+ return "";
+ }
+ const ProfileCompilationInfo::InlineCacheMap* inline_caches = method_info->inline_caches;
+ struct IcLineInfo {
+ bool is_megamorphic_ = false;
+ bool is_missing_types_ = false;
+ std::set<TypeReference> classes_;
+ };
+ std::unordered_map<dex::TypeIndex, IcLineInfo> ics;
+ CodeItemInstructionAccessor accessor(
+ *dex_file,
+ dex_file->GetCodeItem(dex_file->FindCodeItemOffset(*dex_file->FindClassDef(id.class_idx_),
+ dex_method_idx)));
+ for (const auto& [pc, ic_data] : *inline_caches) {
+ const Instruction& inst = accessor.InstructionAt(pc);
+ const dex::MethodId& target = dex_file->GetMethodId(inst.VRegB());
+ if (ic_data.classes.empty() && !ic_data.is_megamorphic && !ic_data.is_missing_types) {
+ continue;
+ }
+ auto val = ics.find(target.class_idx_);
+ if (val == ics.end()) {
+ val = ics.insert({ target.class_idx_, {} }).first;
+ }
+ if (ic_data.is_megamorphic) {
+ val->second.is_megamorphic_ = true;
+ }
+ if (ic_data.is_missing_types) {
+ val->second.is_missing_types_ = true;
+ }
+ for (auto cls : ic_data.classes) {
+ auto it = std::find_if(dex_files->begin(), dex_files->end(), [&](const auto& d) {
+ return method_info->dex_references[cls.dex_profile_index].MatchesDex(&*d);
+ });
+ if (it == dex_files->end()) {
+ val->second.is_missing_types_ = true;
+ continue;
+ }
+ val->second.classes_.insert({ it->get(), cls.type_index });
+ }
+ }
+ if (ics.empty()) {
+ return "";
+ }
+ std::ostringstream dump_ic;
+ dump_ic << kProfileParsingInlineChacheSep;
+ for (const auto& [target, dex_data] : ics) {
+ dump_ic << kProfileParsingInlineChacheTargetSep;
+ dump_ic << dex_file->GetTypeDescriptor(dex_file->GetTypeId(target));
+ if (dex_data.is_missing_types_) {
+ dump_ic << kMissingTypesMarker;
+ } else if (dex_data.is_megamorphic_) {
+ dump_ic << kMegamorphicTypesMarker;
+ } else {
+ bool first = true;
+ for (const auto& klass : dex_data.classes_) {
+ if (!first) {
+ dump_ic << kProfileParsingTypeSep;
+ }
+ first = false;
+ dump_ic << klass.dex_file->GetTypeDescriptor(
+ klass.dex_file->GetTypeId(klass.TypeIndex()));
+ }
+ }
+ }
+ return dump_ic.str();
+ }
+
bool GetClassNamesAndMethods(int fd,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::set<std::string>* out_lines) {
@@ -786,11 +877,10 @@ class ProfMan final {
if (post_startup_methods.find(dex_method_idx) != post_startup_methods.end()) {
flags_string += kMethodFlagStringPostStartup;
}
- out_lines->insert(flags_string +
- type_string +
- kMethodSep +
- method_name +
- signature_string);
+ std::string inline_cache_string =
+ GetInlineCacheLine(profile_info, dex_files, id, dex_file.get(), dex_method_idx);
+ out_lines->insert(flags_string + type_string + kMethodSep + method_name +
+ signature_string + inline_cache_string);
}
}
}
@@ -920,7 +1010,14 @@ class ProfMan final {
// Return true if the definition or a reference 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,
+ const std::string_view& klass_descriptor,
+ /*out*/ TypeReference* class_ref) {
+ return FindClass(
+ ArrayRef<const std::unique_ptr<const DexFile>>(dex_files), klass_descriptor, class_ref);
+ }
+
+ bool FindClass(ArrayRef<const std::unique_ptr<const DexFile>> dex_files,
+ const std::string_view& klass_descriptor,
/*out*/TypeReference* class_ref) {
constexpr uint16_t kInvalidTypeIndex = std::numeric_limits<uint16_t>::max() - 1;
for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) {
@@ -938,7 +1035,7 @@ class ProfMan final {
}
}
- const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor.c_str());
+ const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor);
if (type_id == nullptr) {
continue;
}
@@ -1008,47 +1105,159 @@ class ProfMan final {
return dex_file->GetIndexForMethodId(*method_id);
}
+ template <typename Visitor>
+ void VisitAllInstructions(const TypeReference& class_ref, uint16_t method_idx, Visitor visitor) {
+ const DexFile* dex_file = class_ref.dex_file;
+ uint32_t offset =
+ dex_file->FindCodeItemOffset(*dex_file->FindClassDef(class_ref.TypeIndex()), method_idx);
+ for (const DexInstructionPcPair& inst :
+ CodeItemInstructionAccessor(*dex_file, dex_file->GetCodeItem(offset))) {
+ if (!visitor(inst)) {
+ break;
+ }
+ }
+ }
+
+ // Get dex-pcs of any virtual + interface invokes referencing a method of the
+ // 'target' type in the given method.
+ void GetAllInvokes(const TypeReference& class_ref,
+ uint16_t method_idx,
+ dex::TypeIndex target,
+ /*out*/ std::vector<uint32_t>* dex_pcs) {
+ const DexFile* dex_file = class_ref.dex_file;
+ VisitAllInstructions(class_ref, method_idx, [&](const DexInstructionPcPair& inst) -> bool {
+ switch (inst->Opcode()) {
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE: {
+ const dex::MethodId& meth = dex_file->GetMethodId(inst->VRegB());
+ if (meth.class_idx_ == target) {
+ dex_pcs->push_back(inst.DexPc());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ });
+ }
+
// Given a method, return true if the method has 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 HasSingleInvoke(const TypeReference& class_ref,
uint16_t method_index,
- /*out*/uint32_t* dex_pc) {
- const DexFile* dex_file = class_ref.dex_file;
- uint32_t offset = dex_file->FindCodeItemOffset(
- *dex_file->FindClassDef(class_ref.TypeIndex()),
- method_index);
- const dex::CodeItem* code_item = dex_file->GetCodeItem(offset);
-
+ /*out*/ uint32_t* dex_pc) {
bool found_invoke = false;
- for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(*dex_file, code_item)) {
+ bool found_multiple_invokes = false;
+ VisitAllInstructions(class_ref, method_index, [&](const DexInstructionPcPair& inst) -> bool {
if (inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
- inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) {
+ inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
+ inst->Opcode() == Instruction::INVOKE_INTERFACE ||
+ inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE) {
if (found_invoke) {
LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: "
- << dex_file->PrettyMethod(method_index);
+ << class_ref.dex_file->PrettyMethod(method_index);
return false;
}
found_invoke = true;
*dex_pc = inst.DexPc();
}
- }
+ return true;
+ });
if (!found_invoke) {
- LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << dex_file->PrettyMethod(method_index);
+ LOG(ERROR) << "Could not find any INVOKE_VIRTUAL/INTERFACE: "
+ << class_ref.dex_file->PrettyMethod(method_index);
}
- return found_invoke;
+ return found_invoke && !found_multiple_invokes;
}
+ struct InlineCacheSegment {
+ public:
+ using IcArray =
+ std::array<std::string_view, ProfileCompilationInfo::kIndividualInlineCacheSize + 1>;
+ static void SplitInlineCacheSegment(std::string_view ic_line,
+ /*out*/ std::vector<InlineCacheSegment>* res) {
+ if (ic_line[0] != kProfileParsingInlineChacheTargetSep) {
+ // single target
+ InlineCacheSegment out;
+ Split(ic_line, kProfileParsingTypeSep, &out.inline_caches_);
+ res->push_back(out);
+ return;
+ }
+ std::vector<std::string_view> targets_and_resolutions;
+ // Avoid a zero-length entry.
+ for (std::string_view t :
+ SplitString(ic_line.substr(1), kProfileParsingInlineChacheTargetSep)) {
+ InlineCacheSegment out;
+ DCHECK_EQ(t[0], 'L') << "Target is not a class? " << t;
+ size_t recv_end = t.find_first_of(';');
+ out.receiver_ = t.substr(0, recv_end + 1);
+ Split(t.substr(recv_end + 1), kProfileParsingTypeSep, &out.inline_caches_);
+ res->push_back(out);
+ }
+ }
+
+ bool IsSingleReceiver() const {
+ return !receiver_.has_value();
+ }
+
+ const std::string_view& GetReceiverType() const {
+ DCHECK(!IsSingleReceiver());
+ return *receiver_;
+ }
+
+ const IcArray& GetIcTargets() const {
+ return inline_caches_;
+ }
+
+ size_t NumIcTargets() const {
+ return std::count_if(
+ inline_caches_.begin(), inline_caches_.end(), [](const auto& x) { return !x.empty(); });
+ }
+
+ std::ostream& Dump(std::ostream& os) const {
+ if (!IsSingleReceiver()) {
+ os << "[" << GetReceiverType();
+ }
+ bool first = true;
+ for (std::string_view target : inline_caches_) {
+ if (target.empty()) {
+ break;
+ } else if (!first) {
+ os << ",";
+ }
+ first = false;
+ os << target;
+ }
+ return os;
+ }
+
+ private:
+ std::optional<std::string_view> receiver_;
+ // Max number of ics in the profile file. Don't need to store more than this
+ // (although internally we can have as many as we want). If we fill this up
+ // we are megamorphic.
+ IcArray inline_caches_;
+
+ friend std::ostream& operator<<(std::ostream& os, const InlineCacheSegment& ics);
+ };
+
// 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.
+ // Inline caches are identified by the type of the declared receiver type.
// The possible line formats are:
// "LJustTheClass;".
// "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
// "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,invalid_class".
// "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types".
+ // // Note no ',' after [LTarget;
+ // "LTestInline;->multiInlinePolymorphic(LSuper;)I+[LTarget1;LResA;,LResB;[LTarget2;LResC;,LResD;".
+ // "LTestInline;->multiInlinePolymorphic(LSuper;)I+[LTarget1;LResA;,invalid_class[LTarget2;LResC;,LResD;".
+ // "LTestInline;->multiInlinePolymorphic(LSuper;)I+[LTarget1;missing_types[LTarget2;LResC;,LResD;".
// "{annotation}LTestInline;->inlineNoInlineCaches(LSuper;)I".
// "LTestInline;->*".
// "invalid_class".
@@ -1153,20 +1362,17 @@ class ProfMan final {
// Process the method.
std::string method_spec;
- std::vector<std::string> inline_cache_elems;
// If none of the flags are set, default to hot.
is_hot = is_hot || (!is_hot && !is_startup && !is_post_startup);
std::vector<std::string> method_elems;
- bool is_missing_types = false;
+ // Lifetime of segments is same as method_elems since it contains pointers into the string-data
+ std::vector<InlineCacheSegment> segments;
Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
if (method_elems.size() == 2) {
method_spec = method_elems[0];
- is_missing_types = method_elems[1] == kMissingTypesMarker;
- if (!is_missing_types) {
- Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems);
- }
+ InlineCacheSegment::SplitInlineCacheSegment(method_elems[1], &segments);
} else if (method_elems.size() == 1) {
method_spec = method_elems[0];
} else {
@@ -1180,21 +1386,58 @@ class ProfMan final {
}
std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
- if (is_missing_types || !inline_cache_elems.empty()) {
- uint32_t dex_pc;
- if (!HasSingleInvoke(class_ref, method_index, &dex_pc)) {
- return false;
- }
- std::vector<TypeReference> classes(inline_cache_elems.size(),
- TypeReference(/* dex_file= */ nullptr, dex::TypeIndex()));
- 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;
+ for (const InlineCacheSegment& segment : segments) {
+ std::vector<uint32_t> dex_pcs;
+ if (segment.IsSingleReceiver()) {
+ DCHECK_EQ(segments.size(), 1u);
+ dex_pcs.resize(1, -1);
+ // TODO This single invoke format should really be phased out and removed.
+ if (!HasSingleInvoke(class_ref, method_index, &dex_pcs[0])) {
return false;
}
+ } else {
+ // Get the type-ref the method code will use.
+ std::string receiver_str(segment.GetReceiverType());
+ const dex::TypeId* type_id = class_ref.dex_file->FindTypeId(receiver_str.c_str());
+ if (type_id == nullptr) {
+ LOG(WARNING) << "Could not find class: " << segment.GetReceiverType() << " in dex-file "
+ << class_ref.dex_file << ". Ignoring IC group: '" << segment << "'";
+ continue;
+ }
+ dex::TypeIndex target_index = class_ref.dex_file->GetIndexForTypeId(*type_id);
+
+ GetAllInvokes(class_ref, method_index, target_index, &dex_pcs);
+ }
+ bool missing_types = segment.GetIcTargets()[0] == kMissingTypesMarker;
+ bool megamorphic_types = segment.GetIcTargets()[0] == kMegamorphicTypesMarker;
+ std::vector<TypeReference> classes(
+ missing_types || megamorphic_types ? 0u : segment.NumIcTargets(),
+ TypeReference(/* dex_file= */ nullptr, dex::TypeIndex()));
+ if (!missing_types && !megamorphic_types) {
+ size_t class_it = 0;
+ for (const std::string_view& ic_class : segment.GetIcTargets()) {
+ if (ic_class.empty()) {
+ break;
+ }
+ if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) {
+ LOG(segment.IsSingleReceiver() ? ERROR : WARNING)
+ << "Could not find class: " << ic_class << " in " << segment;
+ if (segment.IsSingleReceiver()) {
+ return false;
+ } else {
+ // Be a bit more forgiving with profiles from servers.
+ missing_types = true;
+ classes.clear();
+ break;
+ }
+ }
+ }
+ // Make sure we are actually the correct size
+ classes.resize(class_it, TypeReference(nullptr, dex::TypeIndex()));
+ }
+ for (size_t dex_pc : dex_pcs) {
+ inline_caches.emplace_back(dex_pc, missing_types, classes, megamorphic_types);
}
- inline_caches.emplace_back(dex_pc, is_missing_types, classes);
}
MethodReference ref(class_ref.dex_file, method_index);
if (is_hot) {
@@ -1340,7 +1583,7 @@ class ProfMan final {
OpenApkFilesFromLocations(&dex_files);
// Process the lines one by one and add the successful ones to the profile.
- ProfileCompilationInfo info;
+ ProfileCompilationInfo info(/*for_boot_image=*/ ShouldCreateBootAndroidProfile());
for (const auto& line : *user_lines) {
ProcessLine(dex_files, line, &info);
@@ -1358,6 +1601,10 @@ class ProfMan final {
return generate_boot_image_profile_;
}
+ bool ShouldCreateBootAndroidProfile() const {
+ return generate_boot_android_profile_;
+ }
+
bool ShouldCreateBootProfile() const {
return generate_boot_profile_;
}
@@ -1526,6 +1773,7 @@ class ProfMan final {
bool dump_only_;
bool dump_classes_and_methods_;
bool generate_boot_image_profile_;
+ bool generate_boot_android_profile_;
bool generate_boot_profile_;
int dump_output_to_fd_;
BootImageOptions boot_image_options_;
@@ -1542,6 +1790,10 @@ class ProfMan final {
std::string preloaded_classes_out_path_;
};
+std::ostream& operator<<(std::ostream& os, const ProfMan::InlineCacheSegment& ics) {
+ return ics.Dump(os);
+}
+
// See ProfileAssistant::ProcessingResult for return codes.
static int profman(int argc, char** argv) {
ProfMan profman;
diff --git a/test/ProfileTestMultiDex/Main.java b/test/ProfileTestMultiDex/Main.java
index 978cb2c6fd..a84cb98543 100644
--- a/test/ProfileTestMultiDex/Main.java
+++ b/test/ProfileTestMultiDex/Main.java
@@ -46,26 +46,58 @@ class TestInline {
public int noInlineCache(Super s) {
return s.getValue();
}
+
+ public int inlineMultiMonomorphic(Super s, Secret sec) {
+ return s.getValue() + sec.getIdentity();
+ }
+
+ public int inlineMultiPolymorphic(Super s, Secret sec) {
+ return s.getValue() + sec.getIdentity();
+ }
+
+ public int inlineTriplePolymorphic(Super s, Secret sec, Secret thr) {
+ return s.getValue() + sec.getIdentity() + thr.getIdentity();
+ }
+
+ public int inlineMultiMegamorphic(Super s, Secret sec) {
+ return s.getValue() + sec.getIdentity();
+ }
+
+ public int inlineMultiMissingTypes(Super s, Secret sec) {
+ return s.getValue() + sec.getIdentity();
+ }
+
+ public int noInlineCacheMulti(Super s, Secret sec) {
+ return s.getValue() + sec.getIdentity();
+ }
+}
+
+abstract class Secret {
+ abstract int getIdentity();
}
-abstract class Super {
+abstract class Super extends Secret {
abstract int getValue();
}
class SubA extends Super {
int getValue() { return 42; }
+ int getIdentity() { return 24; }
}
class SubB extends Super {
int getValue() { return 38; };
+ int getIdentity() { return 83; }
}
class SubD extends Super {
int getValue() { return 20; };
+ int getIdentity() { return 2; };
}
class SubE extends Super {
int getValue() { return 16; };
+ int getIdentity() { return 61; };
}
// Add a class with lots of methods so we can test profile guided compilation triggers.
diff --git a/test/ProfileTestMultiDex/Second.java b/test/ProfileTestMultiDex/Second.java
index a2bb8d4bfd..c9b3c0be04 100644
--- a/test/ProfileTestMultiDex/Second.java
+++ b/test/ProfileTestMultiDex/Second.java
@@ -28,6 +28,7 @@ class Second {
class SubC extends Super {
int getValue() { return 24; }
+ int getIdentity() { return 42; }
}
class TestIntrinsicOatdump {
diff --git a/test/ProfileTestMultiDex/main.jpp b/test/ProfileTestMultiDex/main.jpp
index 0644072510..bd548a88ed 100644
--- a/test/ProfileTestMultiDex/main.jpp
+++ b/test/ProfileTestMultiDex/main.jpp
@@ -4,6 +4,9 @@ Main:
TestInqline:
@@com.android.jack.annotations.ForceInMainDex
class TestInline
+Secret:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Secret
Super:
@@com.android.jack.annotations.ForceInMainDex
class Super
diff --git a/test/ProfileTestMultiDex/main.list b/test/ProfileTestMultiDex/main.list
index 6ca79d4bbe..8ef9280c9e 100644
--- a/test/ProfileTestMultiDex/main.list
+++ b/test/ProfileTestMultiDex/main.list
@@ -1,5 +1,6 @@
Main.class
TestInline.class
+Secret.class
Super.class
SubA.class
SubB.class