diff options
-rw-r--r-- | runtime/parsed_options.cc | 5 | ||||
-rw-r--r-- | runtime/profiler.cc | 204 | ||||
-rw-r--r-- | runtime/profiler.h | 21 | ||||
-rw-r--r-- | runtime/profiler_options.h | 24 | ||||
-rw-r--r-- | runtime/runtime.cc | 3 |
5 files changed, 203 insertions, 54 deletions
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 87106d6..ef20d1c 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -563,6 +563,10 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_change_threshold_)) { return false; } + } else if (option == "-Xprofile-type:method") { + profiler_options_.profile_type_ = kProfilerMethod; + } else if (option == "-Xprofile-type:dexpc") { + profiler_options_.profile_type_ = kProfilerMethodAndDexPC; } else if (StartsWith(option, "-implicit-checks:")) { std::string checks; if (!ParseStringAfterChar(option, ':', &checks)) { @@ -806,6 +810,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xprofile-start-immediately\n"); UsageMessage(stream, " -Xprofile-top-k-threshold:doublevalue\n"); UsageMessage(stream, " -Xprofile-top-k-change-threshold:doublevalue\n"); + UsageMessage(stream, " -Xprofile-type:{method,dexpc}\n"); UsageMessage(stream, " -Xcompiler:filename\n"); UsageMessage(stream, " -Xcompiler-option dex2oat-option\n"); UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 00bb501..2cd876a 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -63,7 +63,8 @@ volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false; static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { BackgroundMethodSamplingProfiler* profiler = reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg); - mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr); + uint32_t dex_pc; + mirror::ArtMethod* method = thread->GetCurrentMethod(&dex_pc); if (false && method == nullptr) { LOG(INFO) << "No current method available"; std::ostringstream os; @@ -71,7 +72,7 @@ static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mu std::string data(os.str()); LOG(INFO) << data; } - profiler->RecordMethod(method); + profiler->RecordMethod(method, dex_pc); } // A closure that is called by the thread checkpoint code. @@ -244,7 +245,7 @@ uint32_t BackgroundMethodSamplingProfiler::WriteProfile() { } // Read the previous profile. - profile_table_.ReadPrevious(fd); + profile_table_.ReadPrevious(fd, options_.GetProfileType()); // Move back to the start of the file. lseek(fd, 0, SEEK_SET); @@ -360,7 +361,7 @@ BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler( // A method has been hit, record its invocation in the method map. // The mutator_lock must be held (shared) when this is called. -void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) { +void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method, uint32_t dex_pc) { if (method == nullptr) { profile_table_.NullMethod(); // Don't record a nullptr method. @@ -393,7 +394,11 @@ void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) { // Add to the profile table unless it is filtered out. if (!is_filtered) { - profile_table_.Put(method); + if (options_.GetProfileType() == kProfilerMethod) { + profile_table_.Put(method); + } else if (options_.GetProfileType() == kProfilerMethodAndDexPC) { + profile_table_.PutDexPC(method, dex_pc); + } } } @@ -403,7 +408,7 @@ void BackgroundMethodSamplingProfiler::CleanProfile() { } uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) { - return profile_table_.Write(os); + return profile_table_.Write(os, options_.GetProfileType()); } // Profile Table. @@ -414,19 +419,18 @@ ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_sampl num_boot_methods_(0) { for (int i = 0; i < kHashSize; i++) { table[i] = nullptr; + dex_table[i] = nullptr; } } ProfileSampleResults::~ProfileSampleResults() { - for (int i = 0; i < kHashSize; i++) { - delete table[i]; - } + Clear(); } // Add a method to the profile table. If it's the first time the method // has been seen, add it with count=1, otherwise increment the count. void ProfileSampleResults::Put(mirror::ArtMethod* method) { - lock_.Lock(Thread::Current()); + MutexLock mu(Thread::Current(), lock_); uint32_t index = Hash(method); if (table[index] == nullptr) { table[index] = new Map(); @@ -438,11 +442,34 @@ void ProfileSampleResults::Put(mirror::ArtMethod* method) { i->second++; } num_samples_++; - lock_.Unlock(Thread::Current()); +} + +// Add a method with dex pc to the profile table +void ProfileSampleResults::PutDexPC(mirror::ArtMethod* method, uint32_t dex_pc) { + MutexLock mu(Thread::Current(), lock_); + uint32_t index = Hash(method); + if (dex_table[index] == nullptr) { + dex_table[index] = new MethodDexPCMap(); + } + MethodDexPCMap::iterator i = dex_table[index]->find(method); + if (i == dex_table[index]->end()) { + DexPCCountMap* dex_pc_map = new DexPCCountMap(); + (*dex_pc_map)[dex_pc] = 1; + (*dex_table[index])[method] = dex_pc_map; + } else { + DexPCCountMap* dex_pc_count = i->second; + DexPCCountMap::iterator dex_pc_i = dex_pc_count->find(dex_pc); + if (dex_pc_i == dex_pc_count->end()) { + (*dex_pc_count)[dex_pc] = 1; + } else { + dex_pc_i->second++; + } + } + num_samples_++; } // Write the profile table to the output stream. Also merge with the previous profile. -uint32_t ProfileSampleResults::Write(std::ostream &os) { +uint32_t ProfileSampleResults::Write(std::ostream& os, ProfileDataType type) { ScopedObjectAccess soa(Thread::Current()); num_samples_ += previous_num_samples_; num_null_methods_ += previous_num_null_methods_; @@ -452,36 +479,101 @@ uint32_t ProfileSampleResults::Write(std::ostream &os) { << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_; os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n"; uint32_t num_methods = 0; - for (int i = 0 ; i < kHashSize; i++) { - Map *map = table[i]; - if (map != nullptr) { - for (const auto &meth_iter : *map) { - mirror::ArtMethod *method = meth_iter.first; - std::string method_name = PrettyMethod(method); - - const DexFile::CodeItem* codeitem = method->GetCodeItem(); - uint32_t method_size = 0; - if (codeitem != nullptr) { - method_size = codeitem->insns_size_in_code_units_; + if (type == kProfilerMethod) { + for (int i = 0 ; i < kHashSize; i++) { + Map *map = table[i]; + if (map != nullptr) { + for (const auto &meth_iter : *map) { + mirror::ArtMethod *method = meth_iter.first; + std::string method_name = PrettyMethod(method); + + const DexFile::CodeItem* codeitem = method->GetCodeItem(); + uint32_t method_size = 0; + if (codeitem != nullptr) { + method_size = codeitem->insns_size_in_code_units_; + } + uint32_t count = meth_iter.second; + + // Merge this profile entry with one from a previous run (if present). Also + // remove the previous entry. + PreviousProfile::iterator pi = previous_.find(method_name); + if (pi != previous_.end()) { + count += pi->second.count_; + previous_.erase(pi); + } + os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size); + ++num_methods; } - uint32_t count = meth_iter.second; - - // Merge this profile entry with one from a previous run (if present). Also - // remove the previous entry. - PreviousProfile::iterator pi = previous_.find(method_name); - if (pi != previous_.end()) { - count += pi->second.count_; - previous_.erase(pi); + } + } + } else if (type == kProfilerMethodAndDexPC) { + for (int i = 0 ; i < kHashSize; i++) { + MethodDexPCMap *dex_map = dex_table[i]; + if (dex_map != nullptr) { + for (const auto &dex_pc_iter : *dex_map) { + mirror::ArtMethod *method = dex_pc_iter.first; + std::string method_name = PrettyMethod(method); + + const DexFile::CodeItem* codeitem = method->GetCodeItem(); + uint32_t method_size = 0; + if (codeitem != nullptr) { + method_size = codeitem->insns_size_in_code_units_; + } + DexPCCountMap* dex_pc_map = dex_pc_iter.second; + uint32_t total_count = 0; + for (const auto &dex_pc_i : *dex_pc_map) { + total_count += dex_pc_i.second; + } + + PreviousProfile::iterator pi = previous_.find(method_name); + if (pi != previous_.end()) { + total_count += pi->second.count_; + DexPCCountMap* previous_dex_pc_map = pi->second.dex_pc_map_; + if (previous_dex_pc_map != nullptr) { + for (const auto &dex_pc_i : *previous_dex_pc_map) { + uint32_t dex_pc = dex_pc_i.first; + uint32_t count = dex_pc_i.second; + DexPCCountMap::iterator di = dex_pc_map->find(dex_pc); + if (di == dex_pc_map->end()) { + (*dex_pc_map)[dex_pc] = count; + } else { + di->second += count; + } + } + } + delete previous_dex_pc_map; + previous_.erase(pi); + } + std::vector<std::string> dex_pc_count_vector; + for (const auto &dex_pc_i : *dex_pc_map) { + dex_pc_count_vector.push_back(StringPrintf("%u:%u", dex_pc_i.first, dex_pc_i.second)); + } + // We write out profile data with dex pc information in the following format: + // "method/total_count/size/[pc_1:count_1,pc_2:count_2,...]". + os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count, + method_size, Join(dex_pc_count_vector, ',').c_str()); + ++num_methods; } - os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size); - ++num_methods; } } } // Now we write out the remaining previous methods. - for (PreviousProfile::iterator pi = previous_.begin(); pi != previous_.end(); ++pi) { - os << StringPrintf("%s/%u/%u\n", pi->first.c_str(), pi->second.count_, pi->second.method_size_); + for (const auto &pi : previous_) { + if (type == kProfilerMethod) { + os << StringPrintf("%s/%u/%u\n", pi.first.c_str(), pi.second.count_, pi.second.method_size_); + } else if (type == kProfilerMethodAndDexPC) { + os << StringPrintf("%s/%u/%u/[", pi.first.c_str(), pi.second.count_, pi.second.method_size_); + DexPCCountMap* previous_dex_pc_map = pi.second.dex_pc_map_; + if (previous_dex_pc_map != nullptr) { + std::vector<std::string> dex_pc_count_vector; + for (const auto &dex_pc_i : *previous_dex_pc_map) { + dex_pc_count_vector.push_back(StringPrintf("%u:%u", dex_pc_i.first, dex_pc_i.second)); + } + os << Join(dex_pc_count_vector, ','); + } + os << "]\n"; + } ++num_methods; } return num_methods; @@ -492,8 +584,20 @@ void ProfileSampleResults::Clear() { num_null_methods_ = 0; num_boot_methods_ = 0; for (int i = 0; i < kHashSize; i++) { - delete table[i]; - table[i] = nullptr; + delete table[i]; + table[i] = nullptr; + if (dex_table[i] != nullptr) { + for (auto &di : *dex_table[i]) { + delete di.second; + di.second = nullptr; + } + } + delete dex_table[i]; + dex_table[i] = nullptr; + } + for (auto &pi : previous_) { + delete pi.second.dex_pc_map_; + pi.second.dex_pc_map_ = nullptr; } previous_.clear(); } @@ -520,7 +624,7 @@ static bool ReadProfileLine(int fd, std::string& line) { return true; } -void ProfileSampleResults::ReadPrevious(int fd) { +void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) { // Reset counters. previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0; @@ -540,21 +644,35 @@ void ProfileSampleResults::ReadPrevious(int fd) { previous_num_null_methods_ = atoi(summary_info[1].c_str()); previous_num_boot_methods_ = atoi(summary_info[2].c_str()); - // Now read each line until the end of file. Each line consists of 3 fields separated by / + // Now read each line until the end of file. Each line consists of 3 or 4 fields separated by / while (true) { if (!ReadProfileLine(fd, line)) { break; } std::vector<std::string> info; Split(line, '/', info); - if (info.size() != 3) { + if (info.size() != 3 && info.size() != 4) { // Malformed. break; } std::string methodname = info[0]; - uint32_t count = atoi(info[1].c_str()); + uint32_t total_count = atoi(info[1].c_str()); uint32_t size = atoi(info[2].c_str()); - previous_[methodname] = PreviousValue(count, size); + DexPCCountMap* dex_pc_map = nullptr; + if (type == kProfilerMethodAndDexPC && info.size() == 4) { + dex_pc_map = new DexPCCountMap(); + std::string dex_pc_counts_str = info[3].substr(1, info[3].size() - 2); + std::vector<std::string> dex_pc_count_pairs; + Split(dex_pc_counts_str, ',', dex_pc_count_pairs); + for (uint32_t i = 0; i < dex_pc_count_pairs.size(); ++i) { + std::vector<std::string> dex_pc_count; + Split(dex_pc_count_pairs[i], ':', dex_pc_count); + uint32_t dex_pc = atoi(dex_pc_count[0].c_str()); + uint32_t count = atoi(dex_pc_count[1].c_str()); + (*dex_pc_map)[dex_pc] = count; + } + } + previous_[methodname] = PreviousValue(total_count, size, dex_pc_map); } } @@ -604,7 +722,7 @@ bool ProfileFile::LoadFile(const std::string& fileName) { } std::vector<std::string> info; Split(line, '/', info); - if (info.size() != 3) { + if (info.size() != 3 && info.size() != 4) { // Malformed. return false; } diff --git a/runtime/profiler.h b/runtime/profiler.h index 0b18dbb..396dd23 100644 --- a/runtime/profiler.h +++ b/runtime/profiler.h @@ -53,8 +53,9 @@ class ProfileSampleResults { ~ProfileSampleResults(); void Put(mirror::ArtMethod* method); - uint32_t Write(std::ostream &os); - void ReadPrevious(int fd); + void PutDexPC(mirror::ArtMethod* method, uint32_t pc); + uint32_t Write(std::ostream &os, ProfileDataType type); + void ReadPrevious(int fd, ProfileDataType type); void Clear(); uint32_t GetNumSamples() { return num_samples_; } void NullMethod() { ++num_null_methods_; } @@ -68,15 +69,21 @@ class ProfileSampleResults { uint32_t num_null_methods_; // Number of samples where can don't know the method. uint32_t num_boot_methods_; // Number of samples in the boot path. - typedef std::map<mirror::ArtMethod*, uint32_t> Map; // Map of method vs its count. + typedef std::map<mirror::ArtMethod*, uint32_t> Map; // Map of method vs its count. Map *table[kHashSize]; + typedef std::map<uint32_t, uint32_t> DexPCCountMap; // Map of dex pc vs its count + // Map of method vs dex pc counts in the method. + typedef std::map<mirror::ArtMethod*, DexPCCountMap*> MethodDexPCMap; + MethodDexPCMap *dex_table[kHashSize]; + struct PreviousValue { - PreviousValue() : count_(0), method_size_(0) {} - PreviousValue(uint32_t count, uint32_t method_size) - : count_(count), method_size_(method_size) {} + PreviousValue() : count_(0), method_size_(0), dex_pc_map_(nullptr) {} + PreviousValue(uint32_t count, uint32_t method_size, DexPCCountMap* dex_pc_map) + : count_(count), method_size_(method_size), dex_pc_map_(dex_pc_map) {} uint32_t count_; uint32_t method_size_; + DexPCCountMap* dex_pc_map_; }; typedef std::map<std::string, PreviousValue> PreviousProfile; @@ -114,7 +121,7 @@ class BackgroundMethodSamplingProfiler { static void Stop() LOCKS_EXCLUDED(Locks::profiler_lock_, wait_lock_); static void Shutdown() LOCKS_EXCLUDED(Locks::profiler_lock_); - void RecordMethod(mirror::ArtMethod *method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void RecordMethod(mirror::ArtMethod *method, uint32_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Barrier& GetBarrier() { return *profiler_barrier_; diff --git a/runtime/profiler_options.h b/runtime/profiler_options.h index 08e32cc..0b63003 100644 --- a/runtime/profiler_options.h +++ b/runtime/profiler_options.h @@ -22,6 +22,11 @@ namespace art { +enum ProfileDataType { + kProfilerMethod, // Method only + kProfilerMethodAndDexPC, // Method with Dex PC +}; + class ProfilerOptions { public: static constexpr bool kDefaultEnabled = false; @@ -32,6 +37,7 @@ class ProfilerOptions { static constexpr bool kDefaultStartImmediately = false; static constexpr double kDefaultTopKThreshold = 90.0; static constexpr double kDefaultChangeInTopKThreshold = 10.0; + static constexpr ProfileDataType kDefaultProfileData = kProfilerMethod; ProfilerOptions() : enabled_(kDefaultEnabled), @@ -41,7 +47,8 @@ class ProfilerOptions { backoff_coefficient_(kDefaultBackoffCoefficient), start_immediately_(kDefaultStartImmediately), top_k_threshold_(kDefaultTopKThreshold), - top_k_change_threshold_(kDefaultChangeInTopKThreshold) {} + top_k_change_threshold_(kDefaultChangeInTopKThreshold), + profile_type_(kDefaultProfileData) {} ProfilerOptions(bool enabled, uint32_t period_s, @@ -50,7 +57,8 @@ class ProfilerOptions { double backoff_coefficient, bool start_immediately, double top_k_threshold, - double top_k_change_threshold): + double top_k_change_threshold, + ProfileDataType profile_type): enabled_(enabled), period_s_(period_s), duration_s_(duration_s), @@ -58,7 +66,8 @@ class ProfilerOptions { backoff_coefficient_(backoff_coefficient), start_immediately_(start_immediately), top_k_threshold_(top_k_threshold), - top_k_change_threshold_(top_k_change_threshold) {} + top_k_change_threshold_(top_k_change_threshold), + profile_type_(profile_type) {} bool IsEnabled() const { return enabled_; @@ -92,6 +101,10 @@ class ProfilerOptions { return top_k_change_threshold_; } + ProfileDataType GetProfileType() const { + return profile_type_; + } + private: friend std::ostream & operator<<(std::ostream &os, const ProfilerOptions& po) { os << "enabled=" << po.enabled_ @@ -101,7 +114,8 @@ class ProfilerOptions { << ", backoff_coefficient=" << po.backoff_coefficient_ << ", start_immediately=" << po.start_immediately_ << ", top_k_threshold=" << po.top_k_threshold_ - << ", top_k_change_threshold=" << po.top_k_change_threshold_; + << ", top_k_change_threshold=" << po.top_k_change_threshold_ + << ", profile_type=" << po.profile_type_; return os; } @@ -123,6 +137,8 @@ class ProfilerOptions { double top_k_threshold_; // How much the top K% samples needs to change in order for the app to be recompiled. double top_k_change_threshold_; + // The type of profile data dumped to the disk. + ProfileDataType profile_type_; }; } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 717381c..8aa7ea1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -421,6 +421,9 @@ bool Runtime::Start() { int fd = open(profile_output_filename_.c_str(), O_RDWR|O_CREAT|O_EXCL, 0660); if (fd >= 0) { close(fd); + } else if (errno != EEXIST) { + LOG(INFO) << "Failed to access the profile file. Profiler disabled."; + return true; } StartProfiler(profile_output_filename_.c_str()); } |