summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWei Jin <wejin@google.com>2014-06-09 16:19:15 -0700
committerCalin Juravle <calin@google.com>2014-06-19 13:05:44 +0100
commita93b0bb9abfbd19fc40ff8ced8818aec1a3464b9 (patch)
tree5ce3679ab53691e97a8971e29f85742a7f95666f
parent995b32cc8e94a9730d6cf663a23afc9c997c1771 (diff)
downloadandroid_art-a93b0bb9abfbd19fc40ff8ced8818aec1a3464b9.tar.gz
android_art-a93b0bb9abfbd19fc40ff8ced8818aec1a3464b9.tar.bz2
android_art-a93b0bb9abfbd19fc40ff8ced8818aec1a3464b9.zip
Add an option to enable collecting dex pc in ART profiler
This CL allows the ART profiler to collect both method signature and dex pc. The type of the profile data is controlled by the option "-Xprofile-type:{method,dexpc}". To avoid conflicting with the original profile data based on method signatures, the new profile data are stored in files with extension ".pc". Change-Id: I8afb2541d386bff77c5b07fc9367d817a79f58e1
-rw-r--r--runtime/parsed_options.cc5
-rw-r--r--runtime/profiler.cc204
-rw-r--r--runtime/profiler.h21
-rw-r--r--runtime/profiler_options.h24
-rw-r--r--runtime/runtime.cc3
5 files changed, 203 insertions, 54 deletions
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 87106d6003..ef20d1ce22 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 00bb50179a..2cd876a5a3 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 0b18dbb90b..396dd23fe3 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 08e32cccbc..0b6300330f 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 717381c2b9..8aa7ea18d1 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());
}