Remove the old obsolete profiler
Bug: 24698874
Test: all the existing run-tests pass.
Change-Id: I56d25c75541275512a64ae5708569461b875df9c
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 131be37..a3e7efa 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -47,7 +47,6 @@
#include "driver/compiler_options.h"
#include "jni_internal.h"
#include "object_lock.h"
-#include "profiler.h"
#include "runtime.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
diff --git a/runtime/Android.mk b/runtime/Android.mk
index aa12c83..bf4e8f1 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -169,7 +169,6 @@
os_linux.cc \
parsed_options.cc \
primitive.cc \
- profiler.cc \
quick_exception_handler.cc \
quick/inline_method_analyser.cc \
reference_table.cc \
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 8c7c966..46be5e6 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -33,7 +33,6 @@
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "os.h"
-#include "profiler.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index bb7b408..d55e373 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -28,7 +28,6 @@
#include "compiler_filter.h"
#include "oat_file.h"
#include "os.h"
-#include "profiler.h"
namespace art {
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
deleted file mode 100644
index 6a77a9e..0000000
--- a/runtime/profiler.cc
+++ /dev/null
@@ -1,920 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "profiler.h"
-
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-
-#include <fstream>
-
-#include "art_method-inl.h"
-#include "base/stl_util.h"
-#include "base/time_utils.h"
-#include "base/unix_file/fd_file.h"
-#include "class_linker.h"
-#include "common_throws.h"
-#include "dex_file-inl.h"
-#include "instrumentation.h"
-#include "mirror/class-inl.h"
-#include "mirror/dex_cache.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
-#include "os.h"
-#include "scoped_thread_state_change.h"
-#include "ScopedLocalRef.h"
-#include "thread.h"
-#include "thread_list.h"
-#include "utils.h"
-
-#include "entrypoints/quick/quick_entrypoints.h"
-
-namespace art {
-
-BackgroundMethodSamplingProfiler* BackgroundMethodSamplingProfiler::profiler_ = nullptr;
-pthread_t BackgroundMethodSamplingProfiler::profiler_pthread_ = 0U;
-volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false;
-
-// TODO: this profiler runs regardless of the state of the machine. Maybe we should use the
-// wakelock or something to modify the run characteristics. This can be done when we
-// have some performance data after it's been used for a while.
-
-// Walk through the method within depth of max_depth_ on the Java stack
-class BoundedStackVisitor : public StackVisitor {
- public:
- BoundedStackVisitor(std::vector<std::pair<ArtMethod*, uint32_t>>* stack,
- Thread* thread,
- uint32_t max_depth)
- SHARED_REQUIRES(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- stack_(stack),
- max_depth_(max_depth),
- depth_(0) {}
-
- bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
- ArtMethod* m = GetMethod();
- if (m->IsRuntimeMethod()) {
- return true;
- }
- uint32_t dex_pc_ = GetDexPc();
- stack_->push_back(std::make_pair(m, dex_pc_));
- ++depth_;
- if (depth_ < max_depth_) {
- return true;
- } else {
- return false;
- }
- }
-
- private:
- std::vector<std::pair<ArtMethod*, uint32_t>>* const stack_;
- const uint32_t max_depth_;
- uint32_t depth_;
-
- DISALLOW_COPY_AND_ASSIGN(BoundedStackVisitor);
-};
-
-// This is called from either a thread list traversal or from a checkpoint. Regardless
-// of which caller, the mutator lock must be held.
-static void GetSample(Thread* thread, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) {
- BackgroundMethodSamplingProfiler* profiler =
- reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
- const ProfilerOptions profile_options = profiler->GetProfilerOptions();
- switch (profile_options.GetProfileType()) {
- case kProfilerMethod: {
- ArtMethod* method = thread->GetCurrentMethod(nullptr);
- if ((false) && method == nullptr) {
- LOG(INFO) << "No current method available";
- std::ostringstream os;
- thread->Dump(os);
- std::string data(os.str());
- LOG(INFO) << data;
- }
- profiler->RecordMethod(method);
- break;
- }
- case kProfilerBoundedStack: {
- std::vector<InstructionLocation> stack;
- uint32_t max_depth = profile_options.GetMaxStackDepth();
- BoundedStackVisitor bounded_stack_visitor(&stack, thread, max_depth);
- bounded_stack_visitor.WalkStack();
- profiler->RecordStack(stack);
- break;
- }
- default:
- LOG(INFO) << "This profile type is not implemented.";
- }
-}
-
-// A closure that is called by the thread checkpoint code.
-class SampleCheckpoint FINAL : public Closure {
- public:
- explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
- profiler_(profiler) {}
-
- void Run(Thread* thread) OVERRIDE {
- Thread* self = Thread::Current();
- if (thread == nullptr) {
- LOG(ERROR) << "Checkpoint with nullptr thread";
- return;
- }
-
- // Grab the mutator lock (shared access).
- ScopedObjectAccess soa(self);
-
- // Grab a sample.
- GetSample(thread, this->profiler_);
-
- // And finally tell the barrier that we're done.
- this->profiler_->GetBarrier().Pass(self);
- }
-
- private:
- BackgroundMethodSamplingProfiler* const profiler_;
-};
-
-bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) {
- MutexLock mu(self, *Locks::profiler_lock_);
- return shutting_down_;
-}
-
-void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
- Runtime* runtime = Runtime::Current();
- BackgroundMethodSamplingProfiler* profiler =
- reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
-
- // Add a random delay for the first time run so that we don't hammer the CPU
- // with all profiles running at the same time.
- const int kRandomDelayMaxSecs = 30;
- const double kMaxBackoffSecs = 24*60*60; // Max backoff time.
-
- srand(MicroTime() * getpid());
- int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup.
-
-
- CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(),
- !runtime->IsAotCompiler()));
-
- Thread* self = Thread::Current();
-
- double backoff = 1.0;
- while (true) {
- if (ShuttingDown(self)) {
- break;
- }
-
- {
- // wait until we need to run another profile
- uint64_t delay_secs = profiler->options_.GetPeriodS() * backoff;
-
- // Add a startup delay to prevent all the profiles running at once.
- delay_secs += startup_delay;
-
- // Immediate startup for benchmarking?
- if (profiler->options_.GetStartImmediately() && startup_delay > 0) {
- delay_secs = 0;
- }
-
- startup_delay = 0;
-
- VLOG(profiler) << "Delaying profile start for " << delay_secs << " secs";
- MutexLock mu(self, profiler->wait_lock_);
- profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0);
- // We were either signaled by Stop or timedout, in either case ignore the timed out result.
-
- // Expand the backoff by its coefficient, but don't go beyond the max.
- backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs);
- }
-
- if (ShuttingDown(self)) {
- break;
- }
-
-
- uint64_t start_us = MicroTime();
- uint64_t end_us = start_us + profiler->options_.GetDurationS() * UINT64_C(1000000);
- uint64_t now_us = start_us;
-
- VLOG(profiler) << "Starting profiling run now for "
- << PrettyDuration((end_us - start_us) * 1000);
-
- SampleCheckpoint check_point(profiler);
-
- size_t valid_samples = 0;
- while (now_us < end_us) {
- if (ShuttingDown(self)) {
- break;
- }
-
- usleep(profiler->options_.GetIntervalUs()); // Non-interruptible sleep.
-
- ThreadList* thread_list = runtime->GetThreadList();
-
- profiler->profiler_barrier_->Init(self, 0);
- size_t barrier_count = thread_list->RunCheckpointOnRunnableThreads(&check_point);
-
- // All threads are suspended, nothing to do.
- if (barrier_count == 0) {
- now_us = MicroTime();
- continue;
- }
-
- valid_samples += barrier_count;
-
- ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
-
- // Wait for the barrier to be crossed by all runnable threads. This wait
- // is done with a timeout so that we can detect problems with the checkpoint
- // running code. We should never see this.
- const uint32_t kWaitTimeoutMs = 10000;
-
- // Wait for all threads to pass the barrier.
- bool timed_out = profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
-
- // We should never get a timeout. If we do, it suggests a problem with the checkpoint
- // code. Crash the process in this case.
- CHECK(!timed_out);
-
- // Update the current time.
- now_us = MicroTime();
- }
-
- if (valid_samples > 0) {
- // After the profile has been taken, write it out.
- ScopedObjectAccess soa(self); // Acquire the mutator lock.
- uint32_t size = profiler->WriteProfile();
- VLOG(profiler) << "Profile size: " << size;
- }
- }
-
- LOG(INFO) << "Profiler shutdown";
- runtime->DetachCurrentThread();
- return nullptr;
-}
-
-// Write out the profile file if we are generating a profile.
-uint32_t BackgroundMethodSamplingProfiler::WriteProfile() {
- std::string full_name = output_filename_;
- VLOG(profiler) << "Saving profile to " << full_name;
-
- int fd = open(full_name.c_str(), O_RDWR);
- if (fd < 0) {
- // Open failed.
- LOG(ERROR) << "Failed to open profile file " << full_name;
- return 0;
- }
-
- // Lock the file for exclusive access. This will block if another process is using
- // the file.
- int err = flock(fd, LOCK_EX);
- if (err < 0) {
- LOG(ERROR) << "Failed to lock profile file " << full_name;
- return 0;
- }
-
- // Read the previous profile.
- profile_table_.ReadPrevious(fd, options_.GetProfileType());
-
- // Move back to the start of the file.
- lseek(fd, 0, SEEK_SET);
-
- // Format the profile output and write to the file.
- std::ostringstream os;
- uint32_t num_methods = DumpProfile(os);
- std::string data(os.str());
- const char *p = data.c_str();
- size_t length = data.length();
- size_t full_length = length;
- do {
- int n = ::write(fd, p, length);
- p += n;
- length -= n;
- } while (length > 0);
-
- // Truncate the file to the new length.
- if (ftruncate(fd, full_length) == -1) {
- LOG(ERROR) << "Failed to truncate profile file " << full_name;
- }
-
- // Now unlock the file, allowing another process in.
- err = flock(fd, LOCK_UN);
- if (err < 0) {
- LOG(ERROR) << "Failed to unlock profile file " << full_name;
- }
-
- // Done, close the file.
- ::close(fd);
-
- // Clean the profile for the next time.
- CleanProfile();
-
- return num_methods;
-}
-
-bool BackgroundMethodSamplingProfiler::Start(
- const std::string& output_filename, const ProfilerOptions& options) {
- if (!options.IsEnabled()) {
- return false;
- }
-
- CHECK(!output_filename.empty());
-
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, *Locks::profiler_lock_);
- // Don't start two profiler threads.
- if (profiler_ != nullptr) {
- return true;
- }
- }
-
- LOG(INFO) << "Starting profiler using output file: " << output_filename
- << " and options: " << options;
- {
- MutexLock mu(self, *Locks::profiler_lock_);
- profiler_ = new BackgroundMethodSamplingProfiler(output_filename, options);
-
- CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread,
- reinterpret_cast<void*>(profiler_)),
- "Profiler thread");
- }
- return true;
-}
-
-
-
-void BackgroundMethodSamplingProfiler::Stop() {
- BackgroundMethodSamplingProfiler* profiler = nullptr;
- pthread_t profiler_pthread = 0U;
- {
- MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_);
- CHECK(!shutting_down_);
- profiler = profiler_;
- shutting_down_ = true;
- profiler_pthread = profiler_pthread_;
- }
-
- // Now wake up the sampler thread if it sleeping.
- {
- MutexLock profile_mu(Thread::Current(), profiler->wait_lock_);
- profiler->period_condition_.Signal(Thread::Current());
- }
- // Wait for the sample thread to stop.
- CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown");
-
- {
- MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
- profiler_ = nullptr;
- }
- delete profiler;
-}
-
-
-void BackgroundMethodSamplingProfiler::Shutdown() {
- Stop();
-}
-
-BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(
- const std::string& output_filename, const ProfilerOptions& options)
- : output_filename_(output_filename),
- options_(options),
- wait_lock_("Profile wait lock"),
- period_condition_("Profile condition", wait_lock_),
- profile_table_(wait_lock_),
- profiler_barrier_(new Barrier(0)) {
- // Populate the filtered_methods set.
- // This is empty right now, but to add a method, do this:
- //
- // filtered_methods_.insert("void java.lang.Object.wait(long, int)");
-}
-
-// Filter out methods the profiler doesn't want to record.
-// We require mutator lock since some statistics will be updated here.
-bool BackgroundMethodSamplingProfiler::ProcessMethod(ArtMethod* method) {
- if (method == nullptr) {
- profile_table_.NullMethod();
- // Don't record a null method.
- return false;
- }
-
- mirror::Class* cls = method->GetDeclaringClass();
- if (cls != nullptr) {
- if (cls->GetClassLoader() == nullptr) {
- // Don't include things in the boot
- profile_table_.BootMethod();
- return false;
- }
- }
-
- bool is_filtered = false;
-
- if (strcmp(method->GetName(), "<clinit>") == 0) {
- // always filter out class init
- is_filtered = true;
- }
-
- // Filter out methods by name if there are any.
- if (!is_filtered && filtered_methods_.size() > 0) {
- std::string method_full_name = PrettyMethod(method);
-
- // Don't include specific filtered methods.
- is_filtered = filtered_methods_.count(method_full_name) != 0;
- }
- return !is_filtered;
-}
-
-// 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(ArtMethod* method) {
- // Add to the profile table unless it is filtered out.
- if (ProcessMethod(method)) {
- profile_table_.Put(method);
- }
-}
-
-// Record the current bounded stack into sampling results.
-void BackgroundMethodSamplingProfiler::RecordStack(const std::vector<InstructionLocation>& stack) {
- if (stack.size() == 0) {
- return;
- }
- // Get the method on top of the stack. We use this method to perform filtering.
- ArtMethod* method = stack.front().first;
- if (ProcessMethod(method)) {
- profile_table_.PutStack(stack);
- }
-}
-
-// Clean out any recordings for the method traces.
-void BackgroundMethodSamplingProfiler::CleanProfile() {
- profile_table_.Clear();
-}
-
-uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) {
- return profile_table_.Write(os, options_.GetProfileType());
-}
-
-// Profile Table.
-// This holds a mapping of ArtMethod* to a count of how many times a sample
-// hit it at the top of the stack.
-ProfileSampleResults::ProfileSampleResults(Mutex& lock)
- : lock_(lock),
- num_samples_(0U),
- num_null_methods_(0U),
- num_boot_methods_(0U),
- previous_num_samples_(0U),
- previous_num_null_methods_(0U),
- previous_num_boot_methods_(0U) {
- for (int i = 0; i < kHashSize; i++) {
- table[i] = nullptr;
- }
- method_context_table = nullptr;
- stack_trie_root_ = nullptr;
-}
-
-ProfileSampleResults::~ProfileSampleResults() {
- 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(ArtMethod* method) {
- MutexLock mu(Thread::Current(), lock_);
- uint32_t index = Hash(method);
- if (table[index] == nullptr) {
- table[index] = new Map();
- }
- Map::iterator i = table[index]->find(method);
- if (i == table[index]->end()) {
- (*table[index])[method] = 1;
- } else {
- i->second++;
- }
- num_samples_++;
-}
-
-// Add a bounded stack to the profile table. Only the count of the method on
-// top of the frame will be increased.
-void ProfileSampleResults::PutStack(const std::vector<InstructionLocation>& stack) {
- MutexLock mu(Thread::Current(), lock_);
- ScopedObjectAccess soa(Thread::Current());
- if (stack_trie_root_ == nullptr) {
- // The root of the stack trie is a dummy node so that we don't have to maintain
- // a collection of tries.
- stack_trie_root_ = new StackTrieNode();
- }
-
- StackTrieNode* current = stack_trie_root_;
- if (stack.size() == 0) {
- current->IncreaseCount();
- return;
- }
-
- for (std::vector<InstructionLocation>::const_reverse_iterator iter = stack.rbegin();
- iter != stack.rend(); ++iter) {
- InstructionLocation inst_loc = *iter;
- ArtMethod* method = inst_loc.first;
- if (method == nullptr) {
- // skip null method
- continue;
- }
- uint32_t dex_pc = inst_loc.second;
- uint32_t method_idx = method->GetDexMethodIndex();
- const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
- MethodReference method_ref(dex_file, method_idx);
- StackTrieNode* child = current->FindChild(method_ref, dex_pc);
- if (child != nullptr) {
- current = child;
- } else {
- uint32_t method_size = 0;
- const DexFile::CodeItem* codeitem = method->GetCodeItem();
- if (codeitem != nullptr) {
- method_size = codeitem->insns_size_in_code_units_;
- }
- StackTrieNode* new_node = new StackTrieNode(method_ref, dex_pc, method_size, current);
- current->AppendChild(new_node);
- current = new_node;
- }
- }
-
- if (current != stack_trie_root_ && current->GetCount() == 0) {
- // Insert into method_context table;
- if (method_context_table == nullptr) {
- method_context_table = new MethodContextMap();
- }
- MethodReference method = current->GetMethod();
- MethodContextMap::iterator i = method_context_table->find(method);
- if (i == method_context_table->end()) {
- TrieNodeSet* node_set = new TrieNodeSet();
- node_set->insert(current);
- (*method_context_table)[method] = node_set;
- } else {
- TrieNodeSet* node_set = i->second;
- node_set->insert(current);
- }
- }
- current->IncreaseCount();
- num_samples_++;
-}
-
-// Write the profile table to the output stream. Also merge with the previous profile.
-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_;
- num_boot_methods_ += previous_num_boot_methods_;
-
- VLOG(profiler) << "Profile: "
- << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_;
- os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n";
- uint32_t num_methods = 0;
- if (type == kProfilerMethod) {
- for (int i = 0 ; i < kHashSize; i++) {
- Map *map = table[i];
- if (map != nullptr) {
- for (const auto &meth_iter : *map) {
- 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;
- }
- }
- }
- } else if (type == kProfilerBoundedStack) {
- if (method_context_table != nullptr) {
- for (const auto &method_iter : *method_context_table) {
- MethodReference method = method_iter.first;
- TrieNodeSet* node_set = method_iter.second;
- std::string method_name = PrettyMethod(method.dex_method_index, *(method.dex_file));
- uint32_t method_size = 0;
- uint32_t total_count = 0;
- PreviousContextMap new_context_map;
- for (const auto &trie_node_i : *node_set) {
- StackTrieNode* node = trie_node_i;
- method_size = node->GetMethodSize();
- uint32_t count = node->GetCount();
- uint32_t dexpc = node->GetDexPC();
- total_count += count;
-
- StackTrieNode* current = node->GetParent();
- // We go backward on the trie to retrieve context and dex_pc until the dummy root.
- // The format of the context is "method_1@pc_1@method_2@pc_2@..."
- std::vector<std::string> context_vector;
- while (current != nullptr && current->GetParent() != nullptr) {
- context_vector.push_back(StringPrintf("%s@%u",
- PrettyMethod(current->GetMethod().dex_method_index, *(current->GetMethod().dex_file)).c_str(),
- current->GetDexPC()));
- current = current->GetParent();
- }
- std::string context_sig = Join(context_vector, '@');
- new_context_map[std::make_pair(dexpc, context_sig)] = count;
- }
-
- PreviousProfile::iterator pi = previous_.find(method_name);
- if (pi != previous_.end()) {
- total_count += pi->second.count_;
- PreviousContextMap* previous_context_map = pi->second.context_map_;
- if (previous_context_map != nullptr) {
- for (const auto &context_i : *previous_context_map) {
- uint32_t count = context_i.second;
- PreviousContextMap::iterator ci = new_context_map.find(context_i.first);
- if (ci == new_context_map.end()) {
- new_context_map[context_i.first] = count;
- } else {
- ci->second += count;
- }
- }
- }
- delete previous_context_map;
- previous_.erase(pi);
- }
- // We write out profile data with dex pc and context information in the following format:
- // "method/total_count/size/[pc_1:count_1:context_1#pc_2:count_2:context_2#...]".
- std::vector<std::string> context_count_vector;
- for (const auto &context_i : new_context_map) {
- context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first,
- context_i.second, context_i.first.second.c_str()));
- }
- os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count,
- method_size, Join(context_count_vector, '#').c_str());
- ++num_methods;
- }
- }
- }
-
- // Now we write out the remaining previous methods.
- 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 == kProfilerBoundedStack) {
- os << StringPrintf("%s/%u/%u/[", pi.first.c_str(), pi.second.count_, pi.second.method_size_);
- PreviousContextMap* previous_context_map = pi.second.context_map_;
- if (previous_context_map != nullptr) {
- std::vector<std::string> context_count_vector;
- for (const auto &context_i : *previous_context_map) {
- context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first,
- context_i.second, context_i.first.second.c_str()));
- }
- os << Join(context_count_vector, '#');
- }
- os << "]\n";
- }
- ++num_methods;
- }
- return num_methods;
-}
-
-void ProfileSampleResults::Clear() {
- num_samples_ = 0;
- num_null_methods_ = 0;
- num_boot_methods_ = 0;
- for (int i = 0; i < kHashSize; i++) {
- delete table[i];
- table[i] = nullptr;
- }
- if (stack_trie_root_ != nullptr) {
- stack_trie_root_->DeleteChildren();
- delete stack_trie_root_;
- stack_trie_root_ = nullptr;
- if (method_context_table != nullptr) {
- delete method_context_table;
- method_context_table = nullptr;
- }
- }
- for (auto &pi : previous_) {
- if (pi.second.context_map_ != nullptr) {
- delete pi.second.context_map_;
- pi.second.context_map_ = nullptr;
- }
- }
- previous_.clear();
-}
-
-uint32_t ProfileSampleResults::Hash(ArtMethod* method) {
- return (PointerToLowMemUInt32(method) >> 3) % kHashSize;
-}
-
-// Read a single line into the given string. Returns true if everything OK, false
-// on EOF or error.
-static bool ReadProfileLine(int fd, std::string& line) {
- char buf[4];
- line.clear();
- while (true) {
- int n = read(fd, buf, 1); // TODO: could speed this up but is it worth it?
- if (n != 1) {
- return false;
- }
- if (buf[0] == '\n') {
- break;
- }
- line += buf[0];
- }
- return true;
-}
-
-void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) {
- // Reset counters.
- previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0;
-
- std::string line;
-
- // The first line contains summary information.
- if (!ReadProfileLine(fd, line)) {
- return;
- }
- std::vector<std::string> summary_info;
- Split(line, '/', &summary_info);
- if (summary_info.size() != 3) {
- // Bad summary info. It should be count/nullcount/bootcount
- return;
- }
- previous_num_samples_ = strtoul(summary_info[0].c_str(), nullptr, 10);
- previous_num_null_methods_ = strtoul(summary_info[1].c_str(), nullptr, 10);
- previous_num_boot_methods_ = strtoul(summary_info[2].c_str(), nullptr, 10);
-
- // 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 && info.size() != 4) {
- // Malformed.
- break;
- }
- std::string methodname = info[0];
- uint32_t total_count = strtoul(info[1].c_str(), nullptr, 10);
- uint32_t size = strtoul(info[2].c_str(), nullptr, 10);
- PreviousContextMap* context_map = nullptr;
- if (type == kProfilerBoundedStack && info.size() == 4) {
- context_map = new PreviousContextMap();
- std::string context_counts_str = info[3].substr(1, info[3].size() - 2);
- std::vector<std::string> context_count_pairs;
- Split(context_counts_str, '#', &context_count_pairs);
- for (uint32_t i = 0; i < context_count_pairs.size(); ++i) {
- std::vector<std::string> context_count;
- Split(context_count_pairs[i], ':', &context_count);
- if (context_count.size() == 2) {
- // Handles the situtation when the profile file doesn't contain context information.
- uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10);
- uint32_t count = strtoul(context_count[1].c_str(), nullptr, 10);
- (*context_map)[std::make_pair(dexpc, "")] = count;
- } else {
- // Handles the situtation when the profile file contains context information.
- uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10);
- uint32_t count = strtoul(context_count[1].c_str(), nullptr, 10);
- std::string context = context_count[2];
- (*context_map)[std::make_pair(dexpc, context)] = count;
- }
- }
- }
- previous_[methodname] = PreviousValue(total_count, size, context_map);
- }
-}
-
-bool ProfileFile::LoadFile(const std::string& fileName) {
- LOG(VERBOSE) << "reading profile file " << fileName;
- struct stat st;
- int err = stat(fileName.c_str(), &st);
- if (err == -1) {
- LOG(VERBOSE) << "not found";
- return false;
- }
- if (st.st_size == 0) {
- return false; // Empty profiles are invalid.
- }
- std::ifstream in(fileName.c_str());
- if (!in) {
- LOG(VERBOSE) << "profile file " << fileName << " exists but can't be opened";
- LOG(VERBOSE) << "file owner: " << st.st_uid << ":" << st.st_gid;
- LOG(VERBOSE) << "me: " << getuid() << ":" << getgid();
- LOG(VERBOSE) << "file permissions: " << std::oct << st.st_mode;
- LOG(VERBOSE) << "errno: " << errno;
- return false;
- }
- // The first line contains summary information.
- std::string line;
- std::getline(in, line);
- if (in.eof()) {
- return false;
- }
- std::vector<std::string> summary_info;
- Split(line, '/', &summary_info);
- if (summary_info.size() != 3) {
- // Bad summary info. It should be total/null/boot.
- return false;
- }
- // This is the number of hits in all profiled methods (without null or boot methods)
- uint32_t total_count = strtoul(summary_info[0].c_str(), nullptr, 10);
-
- // Now read each line until the end of file. Each line consists of 3 fields separated by '/'.
- // Store the info in descending order given by the most used methods.
- typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet;
- ProfileSet countSet;
- while (!in.eof()) {
- std::getline(in, line);
- if (in.eof()) {
- break;
- }
- std::vector<std::string> info;
- Split(line, '/', &info);
- if (info.size() != 3 && info.size() != 4) {
- // Malformed.
- return false;
- }
- int count = atoi(info[1].c_str());
- countSet.insert(std::make_pair(-count, info));
- }
-
- uint32_t curTotalCount = 0;
- ProfileSet::iterator end = countSet.end();
- const ProfileData* prevData = nullptr;
- for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) {
- const std::string& methodname = it->second[0];
- uint32_t count = -it->first;
- uint32_t size = strtoul(it->second[2].c_str(), nullptr, 10);
- double usedPercent = (count * 100.0) / total_count;
-
- curTotalCount += count;
- // Methods with the same count should be part of the same top K percentage bucket.
- double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count)
- ? prevData->GetTopKUsedPercentage()
- : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count);
-
- // Add it to the profile map.
- ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage);
- profile_map_[methodname] = curData;
- prevData = &curData;
- }
- return true;
-}
-
-bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) {
- ProfileMap::iterator i = profile_map_.find(method_name);
- if (i == profile_map_.end()) {
- return false;
- }
- *data = i->second;
- return true;
-}
-
-bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) {
- ProfileMap::iterator end = profile_map_.end();
- for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) {
- if (it->second.GetTopKUsedPercentage() < topKPercentage) {
- topKSamples.insert(it->first);
- }
- }
- return true;
-}
-
-StackTrieNode* StackTrieNode::FindChild(MethodReference method, uint32_t dex_pc) {
- if (children_.size() == 0) {
- return nullptr;
- }
- // Create a dummy node for searching.
- StackTrieNode* node = new StackTrieNode(method, dex_pc, 0, nullptr);
- std::set<StackTrieNode*, StackTrieNodeComparator>::iterator i = children_.find(node);
- delete node;
- return (i == children_.end()) ? nullptr : *i;
-}
-
-void StackTrieNode::DeleteChildren() {
- for (auto &child : children_) {
- if (child != nullptr) {
- child->DeleteChildren();
- delete child;
- }
- }
-}
-
-} // namespace art
diff --git a/runtime/profiler.h b/runtime/profiler.h
deleted file mode 100644
index bd29f71..0000000
--- a/runtime/profiler.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_PROFILER_H_
-#define ART_RUNTIME_PROFILER_H_
-
-#include <memory>
-#include <ostream>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "barrier.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "globals.h"
-#include "instrumentation.h"
-#include "profiler_options.h"
-#include "os.h"
-#include "safe_map.h"
-#include "method_reference.h"
-
-namespace art {
-
-namespace mirror {
- class Class;
-} // namespace mirror
-class ArtMethod;
-class Thread;
-
-typedef std::pair<ArtMethod*, uint32_t> InstructionLocation;
-
-// This class stores the sampled bounded stacks in a trie structure. A path of the trie represents
-// a particular context with the method on top of the stack being a leaf or an internal node of the
-// trie rather than the root.
-class StackTrieNode {
- public:
- StackTrieNode(MethodReference method, uint32_t dex_pc, uint32_t method_size,
- StackTrieNode* parent) :
- parent_(parent), method_(method), dex_pc_(dex_pc),
- count_(0), method_size_(method_size) {
- }
- StackTrieNode() : parent_(nullptr), method_(nullptr, 0),
- dex_pc_(0), count_(0), method_size_(0) {
- }
- StackTrieNode* GetParent() { return parent_; }
- MethodReference GetMethod() { return method_; }
- uint32_t GetCount() { return count_; }
- uint32_t GetDexPC() { return dex_pc_; }
- uint32_t GetMethodSize() { return method_size_; }
- void AppendChild(StackTrieNode* child) { children_.insert(child); }
- StackTrieNode* FindChild(MethodReference method, uint32_t dex_pc);
- void DeleteChildren();
- void IncreaseCount() { ++count_; }
-
- private:
- // Comparator for stack trie node.
- struct StackTrieNodeComparator {
- bool operator()(StackTrieNode* node1, StackTrieNode* node2) const {
- MethodReference mr1 = node1->GetMethod();
- MethodReference mr2 = node2->GetMethod();
- if (mr1.dex_file == mr2.dex_file) {
- if (mr1.dex_method_index == mr2.dex_method_index) {
- return node1->GetDexPC() < node2->GetDexPC();
- } else {
- return mr1.dex_method_index < mr2.dex_method_index;
- }
- } else {
- return mr1.dex_file < mr2.dex_file;
- }
- }
- };
-
- std::set<StackTrieNode*, StackTrieNodeComparator> children_;
- StackTrieNode* parent_;
- MethodReference method_;
- uint32_t dex_pc_;
- uint32_t count_;
- uint32_t method_size_;
-};
-
-//
-// This class holds all the results for all runs of the profiler. It also
-// counts the number of null methods (where we can't determine the method) and
-// the number of methods in the boot path (where we have already compiled the method).
-//
-// This object is an internal profiler object and uses the same locking as the profiler
-// itself.
-class ProfileSampleResults {
- public:
- explicit ProfileSampleResults(Mutex& lock);
- ~ProfileSampleResults();
-
- void Put(ArtMethod* method) REQUIRES(!lock_);
- void PutStack(const std::vector<InstructionLocation>& stack_dump) REQUIRES(!lock_);
- 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_; }
- void BootMethod() { ++num_boot_methods_; }
-
- private:
- uint32_t Hash(ArtMethod* method);
- static constexpr int kHashSize = 17;
- Mutex& lock_; // Reference to the main profiler lock - we don't need two of them.
- uint32_t num_samples_; // Total number of samples taken.
- 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<ArtMethod*, uint32_t> Map; // Map of method vs its count.
- Map *table[kHashSize];
-
- typedef std::set<StackTrieNode*> TrieNodeSet;
- // Map of method hit by profiler vs the set of stack trie nodes for this method.
- typedef std::map<MethodReference, TrieNodeSet*, MethodReferenceComparator> MethodContextMap;
- MethodContextMap *method_context_table;
- StackTrieNode* stack_trie_root_; // Root of the trie that stores sampled stack information.
-
- // Map from <pc, context> to counts.
- typedef std::map<std::pair<uint32_t, std::string>, uint32_t> PreviousContextMap;
- struct PreviousValue {
- PreviousValue() : count_(0), method_size_(0), context_map_(nullptr) {}
- PreviousValue(uint32_t count, uint32_t method_size, PreviousContextMap* context_map)
- : count_(count), method_size_(method_size), context_map_(context_map) {}
- uint32_t count_;
- uint32_t method_size_;
- PreviousContextMap* context_map_;
- };
-
- typedef std::map<std::string, PreviousValue> PreviousProfile;
- PreviousProfile previous_;
- uint32_t previous_num_samples_;
- uint32_t previous_num_null_methods_; // Number of samples where can don't know the method.
- uint32_t previous_num_boot_methods_; // Number of samples in the boot path.
-};
-
-//
-// The BackgroundMethodSamplingProfiler runs in a thread. Most of the time it is sleeping but
-// occasionally wakes up and counts the number of times a method is called. Each time
-// it ticks, it looks at the current method and records it in the ProfileSampleResults
-// table.
-//
-// The timing is controlled by a number of variables:
-// 1. Period: the time between sampling runs.
-// 2. Interval: the time between each sample in a run.
-// 3. Duration: the duration of a run.
-//
-// So the profiler thread is sleeping for the 'period' time. It wakes up and runs for the
-// 'duration'. The run consists of a series of samples, each of which is 'interval' microseconds
-// apart. At the end of a run, it writes the results table to a file and goes back to sleep.
-
-class BackgroundMethodSamplingProfiler {
- public:
- // Start a profile thread with the user-supplied arguments.
- // Returns true if the profile was started or if it was already running. Returns false otherwise.
- static bool Start(const std::string& output_filename, const ProfilerOptions& options)
- REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_,
- !Locks::profiler_lock_);
-
- // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Stop() REQUIRES(!Locks::profiler_lock_, !wait_lock_, !Locks::profiler_lock_)
- NO_THREAD_SAFETY_ANALYSIS;
- // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Shutdown() REQUIRES(!Locks::profiler_lock_) NO_THREAD_SAFETY_ANALYSIS;
-
- void RecordMethod(ArtMethod *method) SHARED_REQUIRES(Locks::mutator_lock_);
- void RecordStack(const std::vector<InstructionLocation>& stack)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool ProcessMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
- const ProfilerOptions& GetProfilerOptions() const { return options_; }
-
- Barrier& GetBarrier() {
- return *profiler_barrier_;
- }
-
- private:
- explicit BackgroundMethodSamplingProfiler(
- const std::string& output_filename, const ProfilerOptions& options);
-
- // The sampling interval in microseconds is passed as an argument.
- // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void* RunProfilerThread(void* arg) REQUIRES(!Locks::profiler_lock_)
- NO_THREAD_SAFETY_ANALYSIS;
-
- uint32_t WriteProfile() SHARED_REQUIRES(Locks::mutator_lock_);
-
- void CleanProfile();
- uint32_t DumpProfile(std::ostream& os) SHARED_REQUIRES(Locks::mutator_lock_);
- static bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
-
- static BackgroundMethodSamplingProfiler* profiler_ GUARDED_BY(Locks::profiler_lock_);
-
- // We need to shut the sample thread down at exit. Setting this to true will do that.
- static volatile bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
-
- // Sampling thread, non-zero when sampling.
- static pthread_t profiler_pthread_;
-
- // Some measure of the number of samples that are significant.
- static constexpr uint32_t kSignificantSamples = 10;
-
- // The name of the file where profile data will be written.
- std::string output_filename_;
- // The options used to start the profiler.
- const ProfilerOptions& options_;
-
-
- // Profile condition support.
- Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
-
- ProfileSampleResults profile_table_;
-
- std::unique_ptr<Barrier> profiler_barrier_;
-
- // Set of methods to be filtered out. This will probably be rare because
- // most of the methods we want to be filtered reside in the boot path and
- // are automatically filtered.
- typedef std::set<std::string> FilteredMethods;
- FilteredMethods filtered_methods_;
-
- DISALLOW_COPY_AND_ASSIGN(BackgroundMethodSamplingProfiler);
-};
-
-//
-// Contains profile data generated from previous runs of the program and stored
-// in a file. It is used to determine whether to compile a particular method or not.
-class ProfileFile {
- public:
- class ProfileData {
- public:
- ProfileData() : count_(0), method_size_(0), used_percent_(0), top_k_used_percentage_(0) {}
- ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size,
- double used_percent, double top_k_used_percentage) :
- method_name_(method_name), count_(count), method_size_(method_size),
- used_percent_(used_percent), top_k_used_percentage_(top_k_used_percentage) {
- // TODO: currently method_size_ is unused
- UNUSED(method_size_);
- }
-
- double GetUsedPercent() const { return used_percent_; }
- uint32_t GetCount() const { return count_; }
- double GetTopKUsedPercentage() const { return top_k_used_percentage_; }
-
- private:
- std::string method_name_; // Method name.
- uint32_t count_; // Number of times it has been called.
- uint32_t method_size_; // Size of the method on dex instructions.
- double used_percent_; // Percentage of how many times this method was called.
- double top_k_used_percentage_; // The percentage of the group that comprise K% of the total
- // used methods this methods belongs to.
- };
-
- public:
- // Loads profile data from the given file. The new data are merged with any existing data.
- // Returns true if the file was loaded successfully and false otherwise.
- bool LoadFile(const std::string& filename);
-
- // Computes the group that comprise top_k_percentage of the total used methods.
- bool GetTopKSamples(std::set<std::string>& top_k_methods, double top_k_percentage);
-
- // If the given method has an entry in the profile table it updates the data
- // and returns true. Otherwise returns false and leaves the data unchanged.
- bool GetProfileData(ProfileData* data, const std::string& method_name);
-
- private:
- // Profile data is stored in a map, indexed by the full method name.
- typedef std::map<std::string, ProfileData> ProfileMap;
- ProfileMap profile_map_;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_PROFILER_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 40e1b13..af70270 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -118,7 +118,6 @@
#include "oat_file_manager.h"
#include "os.h"
#include "parsed_options.h"
-#include "profiler.h"
#include "jit/profile_saver.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"