Change dump-classes profman option to dump-classes-and-methods
The new option is called "--dump-classes-and-methods". This option
dumps methods and classes in the profile as a format that is valid
input for the "--create-profile-from" option.
The goal is to be able to store text based profiles that don't
depend on dex indices or checksums.
Test: mm test-art-host-gtest-profile_assistant_test -j40
Bug: 36457259
Bug: 34927277
Change-Id: Ib966d334897679c1087a8d8d5f28bb3ca5fe3993
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 006d2d6..44cc33f 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -167,12 +167,12 @@
return true;
}
- bool DumpClasses(const std::string& filename, std::string* file_contents) {
+ bool DumpClassesAndMethods(const std::string& filename, std::string* file_contents) {
ScratchFile class_names_file;
std::string profman_cmd = GetProfmanCmd();
std::vector<std::string> argv_str;
argv_str.push_back(profman_cmd);
- argv_str.push_back("--dump-classes");
+ argv_str.push_back("--dump-classes-and-methods");
argv_str.push_back("--profile-file=" + filename);
argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
@@ -196,7 +196,7 @@
profile_file.GetFilename(),
GetLibCoreDexFileNames()[0]));
profile_file.GetFile()->ResetOffset();
- EXPECT_TRUE(DumpClasses(profile_file.GetFilename(), output_file_contents));
+ EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
return true;
}
@@ -478,18 +478,16 @@
std::vector<std::string> class_names = {
"Ljava/lang/Comparable;",
"Ljava/lang/Math;",
- "Ljava/lang/Object;"
+ "Ljava/lang/Object;",
+ "Ljava/lang/Object;-><init>()V"
};
- std::string input_file_contents;
- std::string expected_contents;
+ std::string file_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");
+ file_contents += class_name + std::string("\n");
}
std::string output_file_contents;
- ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
- ASSERT_EQ(output_file_contents, expected_contents);
+ ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
+ ASSERT_EQ(output_file_contents, file_contents);
}
TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
@@ -544,8 +542,8 @@
std::string output_file_contents;
ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
std::string expected_contents =
- DescriptorToDot(class_names[1].c_str()) + std::string("\n") +
- DescriptorToDot(class_names[2].c_str()) + std::string("\n");
+ class_names[1] + std::string("\n") +
+ class_names[2] + std::string("\n");
ASSERT_EQ(output_file_contents, expected_contents);
}
diff --git a/profman/profman.cc b/profman/profman.cc
index dac95b8..c1bdb4f 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -90,8 +90,9 @@
UsageError("");
UsageError(" --dump-output-to-fd=<number>: redirects --dump-only output to a file descriptor.");
UsageError("");
- UsageError(" --dump-classes: dumps a sorted list of classes that are in the specified profile");
- UsageError(" file to standard output (default) in a human readable form.");
+ UsageError(" --dump-classes-and-methods: dumps a sorted list of classes and methods that are");
+ UsageError(" in the specified profile file to standard output (default) in a human");
+ UsageError(" readable form. The output is valid input for --create-profile-from");
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
@@ -118,7 +119,8 @@
UsageError(" --generate-test-profile-class-ratio=<number>: the percentage from the maximum");
UsageError(" number of classes that should be generated. Defaults to 5.");
UsageError("");
- UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes.");
+ UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes and");
+ UsageError(" methods.");
UsageError("");
UsageError(" --dex-location=<string>: location string to use with corresponding");
UsageError(" apk-fd to find dex files");
@@ -151,7 +153,7 @@
ProfMan() :
reference_profile_file_fd_(kInvalidFd),
dump_only_(false),
- dump_classes_(false),
+ dump_classes_and_methods_(false),
dump_output_to_fd_(kInvalidFd),
test_profile_num_dex_(kDefaultTestProfileNumDex),
test_profile_method_ratio_(kDefaultTestProfileMethodRatio),
@@ -184,8 +186,8 @@
}
if (option == "--dump-only") {
dump_only_ = true;
- } else if (option == "--dump-classes") {
- dump_classes_ = true;
+ } else if (option == "--dump-classes-and-methods") {
+ dump_classes_and_methods_ = true;
} else if (option.starts_with("--create-profile-from=")) {
create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString();
} else if (option.starts_with("--dump-output-to-fd=")) {
@@ -406,27 +408,45 @@
return dump_only_;
}
- bool GetClassNames(int fd,
- std::vector<std::unique_ptr<const DexFile>>* dex_files,
- std::set<std::string>* class_names) {
+ bool GetClassNamesAndMethods(int fd,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* out_lines) {
ProfileCompilationInfo profile_info;
if (!profile_info.Load(fd)) {
LOG(ERROR) << "Cannot load profile info";
return false;
}
- profile_info.GetClassNames(dex_files, class_names);
+ for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
+ std::set<dex::TypeIndex> class_types;
+ ProfileCompilationInfo::MethodMap methods;
+ if (profile_info.GetClassesAndMethods(dex_file.get(), &class_types, &methods)) {
+ for (const dex::TypeIndex& type_index : class_types) {
+ const DexFile::TypeId& type_id = dex_file->GetTypeId(type_index);
+ out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id)));
+ }
+ for (const auto& pair : methods) {
+ // TODO: Process inline caches.
+ const uint16_t dex_method_idx = pair.first;
+ const DexFile::MethodId& id = dex_file->GetMethodId(dex_method_idx);
+ std::string signature_string(dex_file->GetMethodSignature(id).ToString());
+ std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_)));
+ std::string method_name(dex_file->GetMethodName(id));
+ out_lines->insert(type_string + kMethodSep + method_name + signature_string);
+ }
+ }
+ }
return true;
}
- bool GetClassNames(const std::string& profile_file,
- std::vector<std::unique_ptr<const DexFile>>* dex_files,
- std::set<std::string>* class_names) {
+ bool GetClassNamesAndMethods(const std::string& profile_file,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* out_lines) {
int fd = open(profile_file.c_str(), O_RDONLY);
if (!FdIsValid(fd)) {
LOG(ERROR) << "Cannot open " << profile_file << strerror(errno);
return false;
}
- if (!GetClassNames(fd, dex_files, class_names)) {
+ if (!GetClassNamesAndMethods(fd, dex_files, out_lines)) {
return false;
}
if (close(fd) < 0) {
@@ -450,26 +470,26 @@
std::set<std::string> class_names;
if (!profile_files_fd_.empty()) {
for (int profile_file_fd : profile_files_fd_) {
- if (!GetClassNames(profile_file_fd, &dex_files, &class_names)) {
+ if (!GetClassNamesAndMethods(profile_file_fd, &dex_files, &class_names)) {
return -1;
}
}
}
if (!profile_files_.empty()) {
for (const std::string& profile_file : profile_files_) {
- if (!GetClassNames(profile_file, &dex_files, &class_names)) {
+ if (!GetClassNamesAndMethods(profile_file, &dex_files, &class_names)) {
return -1;
}
}
}
// Concatenate class names from reference profile file.
if (FdIsValid(reference_profile_file_fd_)) {
- if (!GetClassNames(reference_profile_file_fd_, &dex_files, &class_names)) {
+ if (!GetClassNamesAndMethods(reference_profile_file_fd_, &dex_files, &class_names)) {
return -1;
}
}
if (!reference_profile_file_.empty()) {
- if (!GetClassNames(reference_profile_file_, &dex_files, &class_names)) {
+ if (!GetClassNamesAndMethods(reference_profile_file_, &dex_files, &class_names)) {
return -1;
}
}
@@ -489,8 +509,8 @@
return 0;
}
- bool ShouldOnlyDumpClasses() {
- return dump_classes_;
+ bool ShouldOnlyDumpClassesAndMethods() {
+ return dump_classes_and_methods_;
}
// Read lines from the given file, dropping comments and empty lines. Post-process each line with
@@ -556,17 +576,9 @@
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) {
+ // Find the method specified by method_spec in the class class_ref.
+ uint32_t FindMethodIndex(const ProfileMethodInfo::ProfileClassReference& class_ref,
+ const std::string& method_spec) {
std::vector<std::string> name_and_signature;
Split(method_spec, kProfileParsingFirstCharInSignature, &name_and_signature);
if (name_and_signature.size() != 2) {
@@ -579,38 +591,50 @@
const DexFile::StringId* name_id = dex_file->FindStringId(name.c_str());
if (name_id == nullptr) {
LOG(ERROR) << "Could not find name: " << name;
- return false;
+ return DexFile::kDexNoIndex;
}
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;
+ return DexFile::kDexNoIndex;
}
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;
+ return DexFile::kDexNoIndex;
}
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;
+ return DexFile::kDexNoIndex;
}
- *method_index = dex_file->GetIndexForMethodId(*method_id);
+ return dex_file->GetIndexForMethodId(*method_id);
+ }
+ // 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 ProfileMethodInfo::ProfileClassReference& 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.type_index),
- *method_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;
+ LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: "
+ << dex_file->PrettyMethod(method_index);
return false;
}
found_invoke = true;
@@ -618,7 +642,7 @@
}
}
if (!found_invoke) {
- LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << name;
+ LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << dex_file->PrettyMethod(method_index);
}
return found_invoke;
}
@@ -704,24 +728,29 @@
return false;
}
- uint16_t method_index;
- uint32_t dex_pc;
- if (!FindMethodWithSingleInvoke(class_ref, method_spec, &method_index, &dex_pc)) {
+ const uint32_t method_index = FindMethodIndex(class_ref, method_spec);
+ if (method_index == DexFile::kDexNoIndex) {
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;
+
+ std::vector<ProfileMethodInfo> pmi;
+ 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<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;
+ }
+ }
+ inline_caches.emplace_back(dex_pc, is_missing_types, classes);
}
- std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
- inline_caches.emplace_back(dex_pc, is_missing_types, classes);
- std::vector<ProfileMethodInfo> pmi;
pmi.emplace_back(class_ref.dex_file, method_index, inline_caches);
-
profile->AddMethodsAndClasses(pmi, std::set<DexCacheResolvedClasses>());
return true;
}
@@ -850,7 +879,7 @@
std::string reference_profile_file_;
int reference_profile_file_fd_;
bool dump_only_;
- bool dump_classes_;
+ bool dump_classes_and_methods_;
int dump_output_to_fd_;
std::string test_profile_;
std::string create_profile_from_file_;
@@ -873,7 +902,7 @@
if (profman.ShouldOnlyDumpProfile()) {
return profman.DumpProfileInfo();
}
- if (profman.ShouldOnlyDumpClasses()) {
+ if (profman.ShouldOnlyDumpClassesAndMethods()) {
return profman.DumpClasses();
}
if (profman.ShouldCreateProfile()) {
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 831a24f..6fcf971 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1081,35 +1081,18 @@
return os.str();
}
-void ProfileCompilationInfo::GetClassNames(
- const std::vector<std::unique_ptr<const DexFile>>* dex_files,
- std::set<std::string>* class_names) const {
- std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
- MakeNonOwningVector(dex_files));
- GetClassNames(non_owning_dex_files.get(), class_names);
-}
-
-void ProfileCompilationInfo::GetClassNames(const std::vector<const DexFile*>* dex_files,
- std::set<std::string>* class_names) const {
- if (info_.empty()) {
- return;
+bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile* dex_file,
+ std::set<dex::TypeIndex>* class_set,
+ MethodMap* method_map) const {
+ std::set<std::string> ret;
+ std::string profile_key = GetProfileDexFileKey(dex_file->GetLocation());
+ const DexFileData* dex_data = FindDexData(profile_key);
+ if (dex_data == nullptr || dex_data->checksum != dex_file->GetLocationChecksum()) {
+ return false;
}
- for (const DexFileData* dex_data : info_) {
- const DexFile* dex_file = nullptr;
- if (dex_files != nullptr) {
- for (size_t i = 0; i < dex_files->size(); i++) {
- if (dex_data->profile_key == GetProfileDexFileKey((*dex_files)[i]->GetLocation()) &&
- dex_data->checksum == (*dex_files)[i]->GetLocationChecksum()) {
- dex_file = (*dex_files)[i];
- }
- }
- }
- for (const auto class_it : dex_data->class_set) {
- if (dex_file != nullptr) {
- class_names->insert(std::string(dex_file->PrettyType(class_it)));
- }
- }
- }
+ *method_map = dex_data->method_map;
+ *class_set = dex_data->class_set;
+ return true;
}
bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 955b29d..b8442ea 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -168,6 +168,9 @@
// The inline cache map: DexPc -> DexPcData.
using InlineCacheMap = SafeMap<uint16_t, DexPcData>;
+ // Maps a method dex index to its inline cache.
+ using MethodMap = SafeMap<uint16_t, InlineCacheMap>;
+
// Encodes the full set of inline caches for a given method.
// The dex_references vector is indexed according to the ClassReference::dex_profile_index.
// i.e. the dex file of any ClassReference present in the inline caches can be found at
@@ -234,11 +237,12 @@
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
- void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files,
- std::set<std::string>* class_names) const;
-
- void GetClassNames(const std::vector<const DexFile*>* dex_files,
- std::set<std::string>* class_names) const;
+ // Return the classes and methods for a given dex file through out args. The otu args are the set
+ // of class as well as the methods and their associated inline caches. Returns true if the dex
+ // file is register and has a matching checksum, false otherwise.
+ bool GetClassesAndMethods(const DexFile* dex_file,
+ std::set<dex::TypeIndex>* class_set,
+ MethodMap* method_map) const;
// Perform an equality test with the `other` profile information.
bool Equals(const ProfileCompilationInfo& other);
@@ -273,9 +277,6 @@
kProfileLoadSuccess
};
- // Maps a method dex index to its inline cache.
- using MethodMap = SafeMap<uint16_t, InlineCacheMap>;
-
// Internal representation of the profile information belonging to a dex file.
// Note that we could do without profile_key (the key used to encode the dex
// file in the profile) and profile_index (the index of the dex file in the