Perform profile file analysis in dex2oat
Dex2oat can accept now multiple profile files to drive a profile based
compilation. --profile-file and --reference-profile-file speficy a pair
of profile files which will be evaluated for significant differences
before starting the compilation. If the difference is insignificant
(according to some internal metric) the compilation is skipped and a
message is logged.
Multiple pairs of --profile-file and --reference-profile-file can be
specified. This effectively enables multi user support since profiles
for different users will be kept separately.
--reference-profile-file can be left out, case in which the decision is
solely based on --profile-file. If both flags are present, then their
repetition should form unique pairs.
If the compilation is performed and --reference-profile-file is given
then its data is merged with the data from the corresponding --profile-
file and saved back to the file.
If no profile flags are given, dex2oat proceeds as before and compiles
the dex files unconditionally.
As part of this change
- merge ProfileCompilationInfo and OfflineProfilingInfo under the same
object. There was no use to keep them separate anymore.
- SaveProfilingInfo now merges the data with what was in
the file before instead of overwriting it.
Bug: 26080105
Change-Id: Ia8c8b55587d468bca5179f78941854285426234d
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 3942b0b..a132701 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -30,38 +30,40 @@
namespace art {
-void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
- const std::vector<ArtMethod*>& methods) {
+bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename,
+ const std::vector<ArtMethod*>& methods) {
if (methods.empty()) {
VLOG(profiler) << "No info to save to " << filename;
- return;
+ return true;
}
- DexFileToMethodsMap info;
+ ProfileCompilationInfo info;
+ if (!info.Load(filename)) {
+ LOG(WARNING) << "Could not load previous profile data from file " << filename;
+ return false;
+ }
{
ScopedObjectAccess soa(Thread::Current());
for (auto it = methods.begin(); it != methods.end(); it++) {
- AddMethodInfo(*it, &info);
+ const DexFile* dex_file = (*it)->GetDexFile();
+ if (!info.AddData(dex_file->GetLocation(),
+ dex_file->GetLocationChecksum(),
+ (*it)->GetDexMethodIndex())) {
+ return false;
+ }
}
}
// This doesn't need locking because we are trying to lock the file for exclusive
// access and fail immediately if we can't.
- if (Serialize(filename, info)) {
+ bool result = info.Save(filename);
+ if (result) {
VLOG(profiler) << "Successfully saved profile info to " << filename
<< " Size: " << GetFileSizeBytes(filename);
+ } else {
+ VLOG(profiler) << "Failed to save profile info to " << filename;
}
-}
-
-void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) {
- DCHECK(method != nullptr);
- const DexFile* dex_file = method->GetDexFile();
-
- auto info_it = info->find(dex_file);
- if (info_it == info->end()) {
- info_it = info->Put(dex_file, std::set<uint32_t>());
- }
- info_it->second.insert(method->GetDexMethodIndex());
+ return result;
}
enum OpenMode {
@@ -135,8 +137,7 @@
* /system/priv-app/app/app.apk,131232145,11,23,454,54
* /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1
**/
-bool OfflineProfilingInfo::Serialize(const std::string& filename,
- const DexFileToMethodsMap& info) const {
+bool ProfileCompilationInfo::Save(const std::string& filename) {
int fd = OpenFile(filename, READ_WRITE);
if (fd == -1) {
return false;
@@ -146,14 +147,12 @@
// TODO(calin): Profile this and see how much memory it takes. If too much,
// write to file directly.
std::ostringstream os;
- for (auto it : info) {
- const DexFile* dex_file = it.first;
- const std::set<uint32_t>& method_dex_ids = it.second;
+ for (const auto& it : info_) {
+ const std::string& dex_location = it.first;
+ const DexFileData& dex_data = it.second;
- os << dex_file->GetLocation()
- << kFieldSeparator
- << dex_file->GetLocationChecksum();
- for (auto method_it : method_dex_ids) {
+ os << dex_location << kFieldSeparator << dex_data.checksum;
+ for (auto method_it : dex_data.method_set) {
os << kFieldSeparator << method_it;
}
os << kLineSeparator;
@@ -188,8 +187,22 @@
}
}
-bool ProfileCompilationInfo::ProcessLine(const std::string& line,
- const std::vector<const DexFile*>& dex_files) {
+bool ProfileCompilationInfo::AddData(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_idx) {
+ auto info_it = info_.find(dex_location);
+ if (info_it == info_.end()) {
+ info_it = info_.Put(dex_location, DexFileData(checksum));
+ }
+ if (info_it->second.checksum != checksum) {
+ LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
+ return false;
+ }
+ info_it->second.method_set.insert(method_idx);
+ return true;
+}
+
+bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
std::vector<std::string> parts;
SplitString(line, kFieldSeparator, &parts);
if (parts.size() < 3) {
@@ -203,39 +216,13 @@
return false;
}
- const DexFile* current_dex_file = nullptr;
- for (auto dex_file : dex_files) {
- if (dex_file->GetLocation() == dex_location) {
- if (checksum != dex_file->GetLocationChecksum()) {
- LOG(WARNING) << "Checksum mismatch for "
- << dex_file->GetLocation() << " when parsing " << filename_;
- return false;
- }
- current_dex_file = dex_file;
- break;
- }
- }
- if (current_dex_file == nullptr) {
- return true;
- }
-
for (size_t i = 2; i < parts.size(); i++) {
uint32_t method_idx;
if (!ParseInt(parts[i].c_str(), &method_idx)) {
LOG(WARNING) << "Cannot parse method_idx " << parts[i];
return false;
}
- uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_;
- auto info_it = info_.find(current_dex_file);
- if (info_it == info_.end()) {
- info_it = info_.Put(current_dex_file, ClassToMethodsMap());
- }
- ClassToMethodsMap& class_map = info_it->second;
- auto class_it = class_map.find(class_idx);
- if (class_it == class_map.end()) {
- class_it = class_map.Put(class_idx, std::set<uint32_t>());
- }
- class_it->second.insert(method_idx);
+ AddData(dex_location, checksum, method_idx);
}
return true;
}
@@ -262,25 +249,8 @@
return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
}
-bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) {
- if (dex_files.empty()) {
- return true;
- }
- if (kIsDebugBuild) {
- // In debug builds verify that the locations are unique.
- std::set<std::string> locations;
- for (auto dex_file : dex_files) {
- const std::string& location = dex_file->GetLocation();
- DCHECK(locations.find(location) == locations.end())
- << "DexFiles appear to belong to different apks."
- << " There are multiple dex files with the same location: "
- << location;
- locations.insert(location);
- }
- }
- info_.clear();
-
- int fd = OpenFile(filename_, READ);
+bool ProfileCompilationInfo::Load(const std::string& filename) {
+ int fd = OpenFile(filename, READ);
if (fd == -1) {
return false;
}
@@ -293,7 +263,7 @@
while (success) {
int n = read(fd, buffer, kBufferSize);
if (n < 0) {
- PLOG(WARNING) << "Error when reading profile file " << filename_;
+ PLOG(WARNING) << "Error when reading profile file " << filename;
success = false;
break;
} else if (n == 0) {
@@ -307,7 +277,7 @@
if (current_start_pos == -1) {
break;
}
- if (!ProcessLine(current_line, dex_files)) {
+ if (!ProcessLine(current_line)) {
success = false;
break;
}
@@ -318,25 +288,50 @@
if (!success) {
info_.clear();
}
- return CloseDescriptorForFile(fd, filename_) && success;
+ return CloseDescriptorForFile(fd, filename) && success;
+}
+
+bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+ for (const auto& other_it : other.info_) {
+ const std::string& other_dex_location = other_it.first;
+ const DexFileData& other_dex_data = other_it.second;
+
+ auto info_it = info_.find(other_dex_location);
+ if (info_it == info_.end()) {
+ info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
+ }
+ if (info_it->second.checksum != other_dex_data.checksum) {
+ LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
+ return false;
+ }
+ info_it->second.method_set.insert(other_dex_data.method_set.begin(),
+ other_dex_data.method_set.end());
+ }
+ return true;
}
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
- auto info_it = info_.find(method_ref.dex_file);
+ auto info_it = info_.find(method_ref.dex_file->GetLocation());
if (info_it != info_.end()) {
- uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_;
- const ClassToMethodsMap& class_map = info_it->second;
- auto class_it = class_map.find(class_idx);
- if (class_it != class_map.end()) {
- const std::set<uint32_t>& methods = class_it->second;
- return methods.find(method_ref.dex_method_index) != methods.end();
+ if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
+ return false;
}
- return false;
+ const std::set<uint16_t>& methods = info_it->second.method_set;
+ return methods.find(method_ref.dex_method_index) != methods.end();
}
return false;
}
-std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const {
+uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
+ uint32_t total = 0;
+ for (const auto& it : info_) {
+ total += it.second.method_set.size();
+ }
+ return total;
+}
+
+std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
+ bool print_full_dex_location) const {
std::ostringstream os;
if (info_.empty()) {
return "ProfileInfo: empty";
@@ -344,17 +339,11 @@
os << "ProfileInfo:";
- // Use an additional map to achieve a predefined order based on the dex locations.
- SafeMap<const std::string, const DexFile*> dex_locations_map;
- for (auto info_it : info_) {
- dex_locations_map.Put(info_it.first->GetLocation(), info_it.first);
- }
-
const std::string kFirstDexFileKeySubstitute = ":classes.dex";
- for (auto dex_file_it : dex_locations_map) {
+ for (const auto& it : info_) {
os << "\n";
- const std::string& location = dex_file_it.first;
- const DexFile* dex_file = dex_file_it.second;
+ const std::string& location = it.first;
+ const DexFileData& dex_data = it.second;
if (print_full_dex_location) {
os << location;
} else {
@@ -362,10 +351,19 @@
std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
}
- for (auto class_it : info_.find(dex_file)->second) {
- for (auto method_it : class_it.second) {
- os << "\n " << PrettyMethod(method_it, *dex_file, true);
+ for (const auto method_it : dex_data.method_set) {
+ if (dex_files != nullptr) {
+ const DexFile* dex_file = nullptr;
+ for (size_t i = 0; i < dex_files->size(); i++) {
+ if (location == (*dex_files)[i]->GetLocation()) {
+ dex_file = (*dex_files)[i];
+ }
+ }
+ if (dex_file != nullptr) {
+ os << "\n " << PrettyMethod(method_it, *dex_file, true);
+ }
}
+ os << "\n " << method_it;
}
}
return os.str();
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index 32d4c5b..26e1ac3 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -29,60 +29,50 @@
class ArtMethod;
+// TODO: rename file.
/**
- * Profiling information in a format that can be serialized to disk.
- * It is a serialize-friendly format based on information collected
- * by the interpreter (ProfileInfo).
+ * Profile information in a format suitable to be queried by the compiler and
+ * performing profile guided compilation.
+ * It is a serialize-friendly format based on information collected by the
+ * interpreter (ProfileInfo).
* Currently it stores only the hot compiled methods.
*/
-class OfflineProfilingInfo {
- public:
- void SaveProfilingInfo(const std::string& filename, const std::vector<ArtMethod*>& methods);
-
- private:
- // Map identifying the location of the profiled methods.
- // dex_file_ -> [dex_method_index]+
- using DexFileToMethodsMap = SafeMap<const DexFile*, std::set<uint32_t>>;
-
- void AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const;
-};
-
-/**
- * Profile information in a format suitable to be queried by the compiler and performing
- * profile guided compilation.
- */
class ProfileCompilationInfo {
public:
- // Constructs a ProfileCompilationInfo backed by the provided file.
- explicit ProfileCompilationInfo(const std::string& filename) : filename_(filename) {}
+ static bool SaveProfilingInfo(const std::string& filename,
+ const std::vector<ArtMethod*>& methods);
- // Loads profile information corresponding to the provided dex files.
- // The dex files' multidex suffixes must be unique.
- // This resets the state of the profiling information
- // (i.e. all previously loaded info are cleared).
- bool Load(const std::vector<const DexFile*>& dex_files);
+ // Loads profile information from the given file.
+ bool Load(const std::string& profile_filename);
+ // Loads the data from another ProfileCompilationInfo object.
+ bool Load(const ProfileCompilationInfo& info);
+ // Saves the profile data to the given file.
+ bool Save(const std::string& profile_filename);
+ // Returns the number of methods that were profiled.
+ uint32_t GetNumberOfMethods() const;
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
- const std::string& GetFilename() const { return filename_; }
-
// Dumps all the loaded profile info into a string and returns it.
+ // If dex_files is not null then the method indices will be resolved to their
+ // names.
// This is intended for testing and debugging.
- std::string DumpInfo(bool print_full_dex_location = true) const;
+ std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
+ bool print_full_dex_location = true) const;
private:
- bool ProcessLine(const std::string& line,
- const std::vector<const DexFile*>& dex_files);
+ bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+ bool ProcessLine(const std::string& line);
- using ClassToMethodsMap = SafeMap<uint32_t, std::set<uint32_t>>;
- // Map identifying the location of the profiled methods.
- // dex_file -> class_index -> [dex_method_index]+
- using DexFileToProfileInfoMap = SafeMap<const DexFile*, ClassToMethodsMap>;
+ struct DexFileData {
+ explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
+ uint32_t checksum;
+ std::set<uint16_t> method_set;
+ };
- const std::string filename_;
+ using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>;
+
DexFileToProfileInfoMap info_;
};
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 0278138..ec289ea 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -106,10 +106,9 @@
VLOG(profiler) << "Not enough information to save. Nr of methods: " << methods.size();
return false;
}
- offline_profiling_info_.SaveProfilingInfo(output_filename_, methods);
- VLOG(profiler) << "Saved profile time: " << PrettyDuration(NanoTime() - start);
-
+ ProfileCompilationInfo::SaveProfilingInfo(output_filename_, methods);
+ VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
return true;
}
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 88efd41..d60142b 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -66,7 +66,6 @@
const std::string output_filename_;
jit::JitCodeCache* jit_code_cache_;
const std::set<const std::string> tracked_dex_base_locations_;
- OfflineProfilingInfo offline_profiling_info_;
uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);