summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorDave Allison <dallison@google.com>2014-01-28 18:33:52 -0800
committerDave Allison <dallison@google.com>2014-03-07 13:42:48 -0800
commit39c3bfbd03d85c63cfbe69f17ce5800ccc7d6c13 (patch)
treefa777039b3f7c34f3dd322d04307766246526080 /runtime
parent0918614b7434783477e8668df7850a7aaf8d5611 (diff)
downloadart-39c3bfbd03d85c63cfbe69f17ce5800ccc7d6c13.zip
art-39c3bfbd03d85c63cfbe69f17ce5800ccc7d6c13.tar.gz
art-39c3bfbd03d85c63cfbe69f17ce5800ccc7d6c13.tar.bz2
Make use of profiling information for dex2oat
If the profile file exists, the compiler driver will read it and store the data in an internal map. Then, when we want to work out whether to compile a method or not, the map is consulted and if the method shows up with a high enough percentage of use we compile it. The profile file itself is created by installd and is writeable by the app. The file is in /data/dalvik-cache/profiles and is named by the package name. This also modifies the profiler itself to: 1. Only count runnable threads (not suspended threads) in the profile 2. Use system properties to allow tuning of the profile parameters 3. Merge profiles from multiple processes using file locking. Bug: 12877748 Change-Id: Iab2f3a327a2860db2a80d5724277d6c626227f2b Conflicts: compiler/dex/frontend.cc compiler/dex/mir_analysis.cc compiler/dex/verification_results.cc compiler/driver/compiler_driver.cc dex2oat/dex2oat.cc runtime/class_linker.cc runtime/runtime.cc runtime/runtime.h
Diffstat (limited to 'runtime')
-rw-r--r--runtime/native/dalvik_system_DexFile.cc138
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc12
-rw-r--r--runtime/profiler.cc195
-rw-r--r--runtime/profiler.h27
-rw-r--r--runtime/runtime.cc21
-rw-r--r--runtime/runtime.h9
-rw-r--r--runtime/thread_list.cc30
-rw-r--r--runtime/thread_list.h4
8 files changed, 386 insertions, 50 deletions
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 8a96d79..bab0604 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -15,6 +15,7 @@
*/
#include <unistd.h>
+#include <fcntl.h>
#include "base/logging.h"
#include "class_linker.h"
@@ -36,6 +37,10 @@
#include "toStringArray.h"
#include "zip_archive.h"
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+#endif
+
namespace art {
// A smart pointer that provides read-only access to a Java string's UTF chars.
@@ -193,7 +198,40 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie)
return toStringArray(env, class_names);
}
-static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
+// Copy a profile file
+static void CopyProfileFile(const char* oldfile, const char* newfile) {
+ int fd = open(oldfile, O_RDONLY);
+ if (fd < 0) {
+ // If we can't open the file show the uid:gid of the this process to allow
+ // diagnosis of the problem.
+ LOG(ERROR) << "Failed to open profile file " << oldfile<< ". My uid:gid is "
+ << getuid() << ":" << getgid();
+ return;
+ }
+
+ // Create the copy with rw------- (only accessible by system)
+ int fd2 = open(newfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ if (fd2 < 0) {
+ // If we can't open the file show the uid:gid of the this process to allow
+ // diagnosis of the problem.
+ LOG(ERROR) << "Failed to create/write prev profile file " << newfile << ". My uid:gid is "
+ << getuid() << ":" << getgid();
+ return;
+ }
+ char buf[4096];
+ while (true) {
+ int n = read(fd, buf, sizeof(buf));
+ if (n <= 0) {
+ break;
+ }
+ write(fd2, buf, n);
+ }
+ close(fd);
+ close(fd2);
+}
+
+static jboolean DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
+ jstring javaPkgname, jboolean defer) {
const bool kVerboseLogging = false; // Spammy logging.
const bool kDebugLogging = true; // Logging useful for debugging.
@@ -221,6 +259,97 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename
}
}
+ // Check the profile file. We need to rerun dex2oat if the profile has changed significantly
+ // since the last time, or it's new.
+ // If the 'defer' argument is true then this will be retried later. In this case we
+ // need to make sure that the profile file copy is not made so that we will get the
+ // same result second time.
+ if (javaPkgname != NULL) {
+ ScopedUtfChars pkgname(env, javaPkgname);
+ std::string profile_file = GetDalvikCacheOrDie(GetAndroidData()) + std::string("/profiles/") +
+ pkgname.c_str();
+
+ std::string profile_cache_dir = GetDalvikCacheOrDie(GetAndroidData()) + "/profile-cache";
+
+ // Make the profile cache if it doesn't exist.
+ mkdir(profile_cache_dir.c_str(), 0700);
+
+ // The previous profile file (a copy of the profile the last time this was run) is
+ // in the dalvik-cache directory because this is owned by system. The profiles
+ // directory is owned by install so system cannot write files in there.
+ std::string prev_profile_file = profile_cache_dir + std::string("/") + pkgname.c_str();
+
+ struct stat profstat, prevstat;
+ int e1 = stat(profile_file.c_str(), &profstat);
+ int e2 = stat(prev_profile_file.c_str(), &prevstat);
+
+ if (e1 < 0) {
+ // No profile file, need to run dex2oat
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded profile file " << profile_file << " doesn't exist";
+ }
+ return JNI_TRUE;
+ }
+ if (e2 == 0) {
+ // There is a previous profile file. Check if the profile has changed significantly.
+ // Let's use the file size as a proxy for significance. If the new profile is 10%
+ // different in size than the the old profile then we run dex2oat.
+ double newsize = profstat.st_size;
+ double oldsize = prevstat.st_size;
+ bool need_profile = false;
+
+ double ratio = 0; // If the old file was empty and the new one not
+ if (oldsize > 0 && newsize > 0) {
+ ratio = newsize / oldsize;
+ } else if (oldsize == 0 && newsize > 0) {
+ need_profile = true;
+ } else if (oldsize > 0 && newsize == 0) {
+ // Unlikely to happen, but cover all the bases.
+ need_profile = true;
+ }
+
+ double significant_difference = 10.0;
+#ifdef HAVE_ANDROID_OS
+ // Switch off profiler if the dalvik.vm.profiler property has value 0.
+ char buf[PROP_VALUE_MAX];
+ property_get("dalvik.vm.profiler.dex2oat.threshold", buf, "10.0");
+ significant_difference = strtod(buf, nullptr);
+
+ // Something reasonable?
+ if (significant_difference < 1.0 || significant_difference > 90.0) {
+ significant_difference = 10.0;
+ }
+#endif // The percentage difference that we consider as being significant.
+ double diff_hwm = 1.0 + significant_difference/10.0;
+ double diff_lwm = 1.0 - significant_difference/10.0;
+
+ if (ratio > diff_hwm || ratio < diff_lwm) {
+ need_profile = true;
+ }
+
+ if (need_profile) {
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file <<
+ " is significantly different from old profile file " << prev_profile_file << " (new: " <<
+ newsize << ", old: " << oldsize << ", ratio: " << ratio << ")";
+ }
+ if (!defer) {
+ CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
+ }
+ return JNI_TRUE;
+ }
+ } else {
+ // Previous profile does not exist. Make a copy of the current one.
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded previous profile doesn't exist: " << prev_profile_file;
+ }
+ if (!defer) {
+ CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
+ }
+ return JNI_TRUE;
+ }
+ }
+
// Check if we have an odex file next to the dex file.
std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str()));
std::string error_msg;
@@ -329,11 +458,18 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename
return JNI_FALSE;
}
+// public API, NULL pkgname
+static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass c, jstring javaFilename) {
+ return DexFile_isDexOptNeededInternal(env, c, javaFilename, NULL, false);
+}
+
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, closeDexFile, "(J)V"),
NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
NATIVE_METHOD(DexFile, getClassNameList, "(J)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Z)Z"),
NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;Ljava/lang/String;I)J"),
};
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4aa1d10..0e2d921 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -203,6 +203,7 @@ static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) {
Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
+ Runtime::Current()->UpdateProfilerState(process_state);
}
static void VMRuntime_trimHeap(JNIEnv*, jobject) {
@@ -511,13 +512,16 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
* process name. We use this information to start up the sampling profiler for
* for ART.
*/
-static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring appDir, jstring procName) {
+static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring pkgName, jstring appDir, jstring procName) {
+ const char *pkgNameChars = env->GetStringUTFChars(pkgName, NULL);
const char *appDirChars = env->GetStringUTFChars(appDir, NULL);
const char *procNameChars = env->GetStringUTFChars(procName, NULL);
- std::string profileFile = std::string(appDirChars) + "/art-profile-" + std::string(procNameChars);
- Runtime::Current()->StartProfiler(profileFile.c_str());
+
+ std::string profileFile = StringPrintf("/data/dalvik-cache/profiles/%s", pkgNameChars);
+ Runtime::Current()->StartProfiler(profileFile.c_str(), procNameChars);
env->ReleaseStringUTFChars(appDir, appDirChars);
env->ReleaseStringUTFChars(procName, procNameChars);
+ env->ReleaseStringUTFChars(pkgName, pkgNameChars);
}
static JNINativeMethod gMethods[] = {
@@ -542,7 +546,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
- NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;Ljava/lang/String;)V"),
+ NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"),
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index 20e08b8..da98938 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -17,6 +17,7 @@
#include "profiler.h"
#include <sys/uio.h>
+#include <sys/file.h>
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
@@ -170,6 +171,7 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
SampleCheckpoint check_point(profiler);
+ size_t valid_samples = 0;
while (now_us < end_us) {
if (ShuttingDown(self)) {
break;
@@ -180,7 +182,15 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
ThreadList* thread_list = runtime->GetThreadList();
profiler->profiler_barrier_->Init(self, 0);
- size_t barrier_count = thread_list->RunCheckpoint(&check_point);
+ 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;
ThreadState old_state = self->SetState(kWaitingForCheckPointsToRun);
@@ -206,7 +216,7 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
now_us = MicroTime();
}
- if (!ShuttingDown(self)) {
+ if (valid_samples > 0 && !ShuttingDown(self)) {
// After the profile has been taken, write it out.
ScopedObjectAccess soa(self); // Acquire the mutator lock.
uint32_t size = profiler->WriteProfile();
@@ -221,39 +231,65 @@ void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
// Write out the profile file if we are generating a profile.
uint32_t BackgroundMethodSamplingProfiler::WriteProfile() {
- UniquePtr<File> profile_file;
- Runtime* runtime = Runtime::Current();
- std::string classpath = runtime->GetClassPathString();
- size_t colon = classpath.find(':');
- if (colon != std::string::npos) {
- // More than one file in the classpath. Possible?
- classpath = classpath.substr(0, colon);
- }
-
- std::replace(classpath.begin(), classpath.end(), '/', '@');
std::string full_name = profile_file_name_;
- if (classpath != "") {
- full_name = StringPrintf("%s-%s", profile_file_name_.c_str(), classpath.c_str());
- }
LOG(DEBUG) << "Saving profile to " << full_name;
- profile_file.reset(OS::CreateEmptyFile(full_name.c_str()));
- if (profile_file.get() == nullptr) {
- // Failed to open the profile file, ignore.
- LOG(INFO) << "Failed to op file";
+ 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);
+
+ // 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());
- profile_file->WriteFully(data.c_str(), data.length());
- profile_file->Close();
+ 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.
+ ftruncate(fd, full_length);
+
+ // 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;
}
// Start a profile thread with the user-supplied arguments.
void BackgroundMethodSamplingProfiler::Start(int period, int duration,
- std::string profile_file_name, int interval_us,
+ const std::string& profile_file_name, const std::string& procName,
+ int interval_us,
double backoff_coefficient, bool startImmediately) {
Thread* self = Thread::Current();
{
@@ -266,12 +302,14 @@ void BackgroundMethodSamplingProfiler::Start(int period, int duration,
// Only on target...
#ifdef HAVE_ANDROID_OS
- // Switch off profiler if the dalvik.vm.profiler property has value 0.
- char buf[PROP_VALUE_MAX];
- property_get("dalvik.vm.profiler", buf, "0");
- if (strcmp(buf, "0") == 0) {
- LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1";
- return;
+ if (!startImmediately) {
+ // Switch off profiler if the dalvik.vm.profiler property has value 0.
+ char buf[PROP_VALUE_MAX];
+ property_get("dalvik.vm.profiler", buf, "0");
+ if (strcmp(buf, "0") == 0) {
+ LOG(INFO) << "Profiler disabled. To enable setprop dalvik.vm.profiler 1";
+ return;
+ }
}
#endif
@@ -281,6 +319,7 @@ void BackgroundMethodSamplingProfiler::Start(int period, int duration,
{
MutexLock mu(self, *Locks::profiler_lock_);
profiler_ = new BackgroundMethodSamplingProfiler(period, duration, profile_file_name,
+ procName,
backoff_coefficient,
interval_us, startImmediately);
@@ -323,9 +362,10 @@ void BackgroundMethodSamplingProfiler::Shutdown() {
}
BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(int period, int duration,
- std::string profile_file_name,
+ const std::string& profile_file_name,
+ const std::string& process_name,
double backoff_coefficient, int interval_us, bool startImmediately)
- : profile_file_name_(profile_file_name),
+ : profile_file_name_(profile_file_name), process_name_(process_name),
period_s_(period), start_immediately_(startImmediately),
interval_us_(interval_us), backoff_factor_(1.0),
backoff_coefficient_(backoff_coefficient), duration_s_(duration),
@@ -423,9 +463,13 @@ void ProfileSampleResults::Put(mirror::ArtMethod* method) {
lock_.Unlock(Thread::Current());
}
-// Write the profile table to the output stream.
+// Write the profile table to the output stream. Also merge with the previous profile.
uint32_t ProfileSampleResults::Write(std::ostream &os) {
ScopedObjectAccess soa(Thread::Current());
+ num_samples_ += previous_num_samples_;
+ num_null_methods_ += previous_num_null_methods_;
+ num_boot_methods_ += previous_num_boot_methods_;
+
LOG(DEBUG) << "Profile: " << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_;
os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n";
uint32_t num_methods = 0;
@@ -433,14 +477,35 @@ uint32_t ProfileSampleResults::Write(std::ostream &os) {
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);
- uint32_t method_size = method->GetCodeSize();
- os << StringPrintf("%s/%u/%u\n", method_name.c_str(), meth_iter.second, method_size);
- ++num_methods;
- }
+ mirror::ArtMethod *method = meth_iter.first;
+ std::string method_name = PrettyMethod(method);
+
+ MethodHelper mh(method);
+ const DexFile::CodeItem* codeitem = mh.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;
+ }
}
}
+
+ // 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_);
+ ++num_methods;
+ }
return num_methods;
}
@@ -452,11 +517,67 @@ void ProfileSampleResults::Clear() {
delete table[i];
table[i] = nullptr;
}
+ previous_.clear();
}
uint32_t ProfileSampleResults::Hash(mirror::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) {
+ // 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_ = atoi(summary_info[0].c_str());
+ 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 /
+ while (true) {
+ if (!ReadProfileLine(fd, line)) {
+ break;
+ }
+ std::vector<std::string> info;
+ Split(line, '/', info);
+ if (info.size() != 3) {
+ // Malformed.
+ break;
+ }
+ std::string methodname = info[0];
+ uint32_t count = atoi(info[1].c_str());
+ uint32_t size = atoi(info[2].c_str());
+ previous_[methodname] = PreviousValue(count, size);
+ }
+}
} // namespace art
diff --git a/runtime/profiler.h b/runtime/profiler.h
index 6ea6c84..b03b170 100644
--- a/runtime/profiler.h
+++ b/runtime/profiler.h
@@ -54,10 +54,12 @@ class ProfileSampleResults {
void Put(mirror::ArtMethod* method);
uint32_t Write(std::ostream &os);
+ void ReadPrevious(int fd);
void Clear();
uint32_t GetNumSamples() { return num_samples_; }
void NullMethod() { ++num_null_methods_; }
void BootMethod() { ++num_boot_methods_; }
+
private:
uint32_t Hash(mirror::ArtMethod* method);
static constexpr int kHashSize = 17;
@@ -68,6 +70,19 @@ class ProfileSampleResults {
typedef std::map<mirror::ArtMethod*, uint32_t> Map; // Map of method vs its count.
Map *table[kHashSize];
+
+ struct PreviousValue {
+ PreviousValue() : count_(0), method_size_(0) {}
+ PreviousValue(uint32_t count, uint32_t method_size) : count_(count), method_size_(method_size) {}
+ uint32_t count_;
+ uint32_t method_size_;
+ };
+
+ 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.
};
//
@@ -87,7 +102,8 @@ class ProfileSampleResults {
class BackgroundMethodSamplingProfiler {
public:
- static void Start(int period, int duration, std::string profile_filename, int interval_us,
+ static void Start(int period, int duration, const std::string& profile_filename,
+ const std::string& procName, int interval_us,
double backoff_coefficient, bool startImmediately)
LOCKS_EXCLUDED(Locks::mutator_lock_,
Locks::thread_list_lock_,
@@ -104,8 +120,10 @@ class BackgroundMethodSamplingProfiler {
}
private:
- explicit BackgroundMethodSamplingProfiler(int period, int duration, std::string profile_filename,
- double backoff_coefficient, int interval_us, bool startImmediately);
+ explicit BackgroundMethodSamplingProfiler(int period, int duration,
+ const std::string& profile_filename,
+ const std::string& process_name,
+ double backoff_coefficient, int interval_us, bool startImmediately);
// The sampling interval in microseconds is passed as an argument.
static void* RunProfilerThread(void* arg) LOCKS_EXCLUDED(Locks::profiler_lock_);
@@ -130,6 +148,9 @@ class BackgroundMethodSamplingProfiler {
// File to write profile data out to. Cannot be empty if we are profiling.
std::string profile_file_name_;
+ // Process name.
+ std::string process_name_;
+
// Number of seconds between profile runs.
uint32_t period_s_;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index fdbf245..d1c8370 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -27,6 +27,7 @@
#include <cstdlib>
#include <limits>
#include <vector>
+#include <fcntl.h>
#include "arch/arm/registers_arm.h"
#include "arch/mips/registers_mips.h"
@@ -69,6 +70,10 @@
#include "JniConstants.h" // Last to avoid LOG redefinition in ics-mr1-plus-art.
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+#endif
+
namespace art {
Runtime* Runtime::instance_ = NULL;
@@ -370,7 +375,12 @@ bool Runtime::Start() {
if (profile_) {
// User has asked for a profile using -Xprofile
- StartProfiler(profile_output_filename_.c_str(), true);
+ // Create the profile file if it doesn't exist.
+ int fd = open(profile_output_filename_.c_str(), O_RDWR|O_CREAT|O_EXCL, 0660);
+ if (fd >= 0) {
+ close(fd);
+ }
+ StartProfiler(profile_output_filename_.c_str(), "", true);
}
return true;
@@ -1055,10 +1065,10 @@ void Runtime::RemoveMethodVerifier(verifier::MethodVerifier* verifier) {
method_verifiers_.erase(it);
}
-void Runtime::StartProfiler(const char *appDir, bool startImmediately) {
+void Runtime::StartProfiler(const char* appDir, const char* procName, bool startImmediately) {
BackgroundMethodSamplingProfiler::Start(profile_period_s_, profile_duration_s_, appDir,
- profile_interval_us_, profile_backoff_coefficient_,
- startImmediately);
+ procName, profile_interval_us_,
+ profile_backoff_coefficient_, startImmediately);
}
// Transaction support.
@@ -1136,4 +1146,7 @@ void Runtime::SetFaultMessage(const std::string& message) {
fault_message_ = message;
}
+void Runtime::UpdateProfilerState(int state) {
+ LOG(DEBUG) << "Profiler state updated to " << state;
+}
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 65d296a..109f031 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -373,7 +373,8 @@ class Runtime {
const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader);
void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path);
- void StartProfiler(const char *appDir, bool startImmediately = false);
+ void StartProfiler(const char* appDir, const char* procName, bool startImmediately = false);
+ void UpdateProfilerState(int state);
// Transaction support.
bool IsActiveTransaction() const;
@@ -419,6 +420,12 @@ class Runtime {
void StartDaemonThreads();
void StartSignalCatcher();
+ // NOTE: these must match the gc::ProcessState values as they come directly
+ // from the framework.
+ static constexpr int kProfileForground = 0;
+ static constexpr int kProfileBackgrouud = 1;
+
+
// A pointer to the active runtime or NULL.
static Runtime* instance_;
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index bddebbd..ac5750b 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -269,6 +269,36 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
return count + suspended_count_modified_threads.size() + 1;
}
+// Request that a checkpoint function be run on all active (non-suspended)
+// threads. Returns the number of successful requests.
+size_t ThreadList::RunCheckpointOnRunnableThreads(Closure* checkpoint_function) {
+ Thread* self = Thread::Current();
+ if (kIsDebugBuild) {
+ Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+ Locks::thread_list_lock_->AssertNotHeld(self);
+ Locks::thread_suspend_count_lock_->AssertNotHeld(self);
+ CHECK_NE(self->GetState(), kRunnable);
+ }
+
+ size_t count = 0;
+ {
+ // Call a checkpoint function for each non-suspended thread.
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ for (const auto& thread : list_) {
+ if (thread != self) {
+ if (thread->RequestCheckpoint(checkpoint_function)) {
+ // This thread will run its checkpoint some time in the near future.
+ count++;
+ }
+ }
+ }
+ }
+
+ // Return the number of threads that will run the checkpoint function.
+ return count;
+}
+
void ThreadList::SuspendAll() {
Thread* self = Thread::Current();
DCHECK(self != nullptr);
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 1a76705..58bd92a 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -90,6 +90,10 @@ class ThreadList {
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
+ size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function);
+ LOCKS_EXCLUDED(Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
+
// Suspends all threads
void SuspendAllForDebugger()
LOCKS_EXCLUDED(Locks::mutator_lock_,