summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 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());
}