Merge "Add support for profiling boot class path"
am: 9533cc0a37
Change-Id: I3e44aa9aefc79084b7af226e4f5fd0bc6908ec6f
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 07639e8..b224ec7 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -484,7 +484,7 @@
* -Xps-*
*/
TEST_F(CmdlineParserTest, ProfileSaverOptions) {
- ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc");
+ ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc", true);
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xjitsaveprofilinginfo "
@@ -495,7 +495,8 @@
"-Xps-min-classes-to-save:5 "
"-Xps-min-notification-before-wake:6 "
"-Xps-max-notification-before-wake:7 "
- "-Xps-profile-path:abc",
+ "-Xps-profile-path:abc "
+ "-Xps-profile-boot-class-path",
M::ProfileSaverOpts);
} // TEST_F
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 185a0e4..4de8a48 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -712,6 +712,11 @@
return Result::SuccessNoValue();
}
+ if (option == "profile-boot-class-path") {
+ existing.profile_boot_class_path_ = true;
+ return Result::SuccessNoValue();
+ }
+
// The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index edce9cd..2cf7d9f 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -194,21 +194,24 @@
GetClassesAndMethodsVisitor(MethodReferenceCollection* hot_methods,
MethodReferenceCollection* sampled_methods,
TypeReferenceCollection* resolved_classes,
- uint32_t hot_method_sample_threshold)
+ uint32_t hot_method_sample_threshold,
+ bool profile_boot_class_path)
: hot_methods_(hot_methods),
sampled_methods_(sampled_methods),
resolved_classes_(resolved_classes),
- hot_method_sample_threshold_(hot_method_sample_threshold) {}
+ hot_method_sample_threshold_(hot_method_sample_threshold),
+ profile_boot_class_path_(profile_boot_class_path) {}
virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
if (klass->IsProxyClass() ||
klass->IsArrayClass() ||
+ klass->IsPrimitive() ||
!klass->IsResolved() ||
klass->IsErroneousResolved() ||
- klass->GetClassLoader() == nullptr) {
+ (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) {
return true;
}
- DCHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
+ CHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
resolved_classes_->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
if (!method.IsNative()) {
@@ -235,6 +238,7 @@
MethodReferenceCollection* const sampled_methods_;
TypeReferenceCollection* const resolved_classes_;
uint32_t hot_method_sample_threshold_;
+ const bool profile_boot_class_path_;
};
void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
@@ -263,7 +267,8 @@
GetClassesAndMethodsVisitor visitor(&hot_methods,
&startup_methods,
&resolved_classes,
- hot_threshold);
+ hot_threshold,
+ options_.GetProfileBootClassPath());
runtime->GetClassLinker()->VisitClasses(&visitor);
}
}
@@ -469,24 +474,49 @@
const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths) {
+ Runtime* const runtime = Runtime::Current();
DCHECK(options.IsEnabled());
- DCHECK(Runtime::Current()->GetJit() != nullptr);
+ DCHECK(runtime->GetJit() != nullptr);
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
std::vector<std::string> code_paths_to_profile;
-
for (const std::string& location : code_paths) {
if (ShouldProfileLocation(location)) {
code_paths_to_profile.push_back(location);
}
}
+
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ // Support getting profile samples for the boot class path. This will be used to generate the boot
+ // image profile. The intention is to use this code to generate to boot image but not use it in
+ // production. b/37966211
+ if (options.GetProfileBootClassPath()) {
+ std::set<std::string> code_paths_keys;
+ for (const std::string& location : code_paths) {
+ code_paths_keys.insert(ProfileCompilationInfo::GetProfileDexFileKey(location));
+ }
+ for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
+ // Don't check ShouldProfileLocation since the boot class path may be speed compiled.
+ const std::string& location = dex_file->GetLocation();
+ const std::string key = ProfileCompilationInfo::GetProfileDexFileKey(location);
+ VLOG(profiler) << "Registering boot dex file " << location;
+ if (code_paths_keys.find(key) != code_paths_keys.end()) {
+ LOG(WARNING) << "Boot class path location key conflicts with code path " << location;
+ } else if (instance_ == nullptr) {
+ // Only add the boot class path once since Start may be called multiple times for secondary
+ // dexes.
+ // We still do the collision check above. This handles any secondary dexes that conflict
+ // with the boot class path dex files.
+ code_paths_to_profile.push_back(location);
+ }
+ }
+ }
if (code_paths_to_profile.empty()) {
VLOG(profiler) << "No code paths should be profiled.";
return;
}
- MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
if (instance_ != nullptr) {
// If we already have an instance, make sure it uses the same jit_code_cache.
// This may be called multiple times via Runtime::registerAppInfo (e.g. for
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index 44550f4..251227e 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -40,7 +40,8 @@
min_classes_to_save_(kMinClassesToSave),
min_notification_before_wake_(kMinNotificationBeforeWake),
max_notification_before_wake_(kMaxNotificationBeforeWake),
- profile_path_("") {}
+ profile_path_(""),
+ profile_boot_class_path_(false) {}
ProfileSaverOptions(
bool enabled,
@@ -51,8 +52,9 @@
uint32_t min_classes_to_save,
uint32_t min_notification_before_wake,
uint32_t max_notification_before_wake,
- const std::string& profile_path):
- enabled_(enabled),
+ const std::string& profile_path,
+ bool profile_boot_class_path)
+ : enabled_(enabled),
min_save_period_ms_(min_save_period_ms),
save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
hot_startup_method_samples_(hot_startup_method_samples),
@@ -60,7 +62,8 @@
min_classes_to_save_(min_classes_to_save),
min_notification_before_wake_(min_notification_before_wake),
max_notification_before_wake_(max_notification_before_wake),
- profile_path_(profile_path) {}
+ profile_path_(profile_path),
+ profile_boot_class_path_(profile_boot_class_path) {}
bool IsEnabled() const {
return enabled_;
@@ -97,6 +100,9 @@
std::string GetProfilePath() const {
return profile_path_;
}
+ bool GetProfileBootClassPath() const {
+ return profile_boot_class_path_;
+ }
friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
os << "enabled_" << pso.enabled_
@@ -106,7 +112,8 @@
<< ", min_methods_to_save_" << pso.min_methods_to_save_
<< ", min_classes_to_save_" << pso.min_classes_to_save_
<< ", min_notification_before_wake_" << pso.min_notification_before_wake_
- << ", max_notification_before_wake_" << pso.max_notification_before_wake_;
+ << ", max_notification_before_wake_" << pso.max_notification_before_wake_
+ << ", profile_boot_class_path_" << pso.profile_boot_class_path_;
return os;
}
@@ -121,6 +128,7 @@
uint32_t min_notification_before_wake_;
uint32_t max_notification_before_wake_;
std::string profile_path_;
+ bool profile_boot_class_path_;
};
} // namespace art
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index 019ddad..0bdbade 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -22,63 +22,41 @@
#include "jni.h"
#include "method_reference.h"
#include "mirror/class-inl.h"
+#include "mirror/executable.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "scoped_thread_state_change-inl.h"
#include "ScopedUtfChars.h"
-#include "stack.h"
#include "thread.h"
namespace art {
namespace {
-class CreateProfilingInfoVisitor : public StackVisitor {
- public:
- explicit CreateProfilingInfoVisitor(Thread* thread, const char* method_name)
- REQUIRES_SHARED(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- method_name_(method_name) {}
-
- bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* m = GetMethod();
- std::string m_name(m->GetName());
-
- if (m_name.compare(method_name_) == 0) {
- ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
- method_index_ = m->GetDexMethodIndex();
- return false;
- }
- return true;
- }
-
- int method_index_ = -1;
- const char* const method_name_;
-};
-
-extern "C" JNIEXPORT jint JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
jclass,
- jstring method_name) {
- ScopedUtfChars chars(env, method_name);
- CHECK(chars.c_str() != nullptr);
- ScopedObjectAccess soa(Thread::Current());
- CreateProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
- visitor.WalkStack();
- return visitor.method_index_;
+ jobject method) {
+ CHECK(method != nullptr);
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+ ProfilingInfo::Create(soa.Self(), exec->GetArtMethod(), /* retry_allocation */ true);
}
extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
ProfileSaver::ForceProcessProfiles();
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(
- JNIEnv* env, jclass cls, jstring filename, jint method_index) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
+ jclass,
+ jstring filename,
+ jobject method) {
ScopedUtfChars filename_chars(env, filename);
CHECK(filename_chars.c_str() != nullptr);
- ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex_file = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetDexFile();
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+ ArtMethod* art_method = exec->GetArtMethod();
return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
- dex_file,
- static_cast<uint16_t>(method_index));
+ art_method->GetDexFile(),
+ art_method->GetDexMethodIndex());
}
} // namespace
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
index fce6ac1..055035b 100644
--- a/test/595-profile-saving/run
+++ b/test/595-profile-saving/run
@@ -24,4 +24,5 @@
--runtime-option '-Xcompiler-option --compiler-filter=quicken' \
--runtime-option -Xjitsaveprofilinginfo \
--runtime-option -Xusejit:false \
+ --runtime-option -Xps-profile-boot-class-path \
"${@}"
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
index faf94c4..18c0598 100644
--- a/test/595-profile-saving/src/Main.java
+++ b/test/595-profile-saving/src/Main.java
@@ -31,11 +31,17 @@
VMRuntime.registerAppInfo(file.getPath(),
new String[] {codePath});
- int methodIdx = $opt$noinline$testProfile();
- ensureProfileProcessing();
- if (!presentInProfile(file.getPath(), methodIdx)) {
- throw new RuntimeException("Method with index " + methodIdx + " not in the profile");
+ // Test that the profile saves an app method with a profiling info.
+ Method appMethod = Main.class.getDeclaredMethod("testAddMethodToProfile",
+ File.class, Method.class);
+ testAddMethodToProfile(file, appMethod);
+
+ // Test that the profile saves a boot class path method with a profiling info.
+ Method bootMethod = File.class.getDeclaredMethod("delete");
+ if (bootMethod.getDeclaringClass().getClassLoader() != Object.class.getClassLoader()) {
+ System.out.println("Class loader does not match boot class");
}
+ testAddMethodToProfile(file, bootMethod);
} finally {
if (file != null) {
file.delete();
@@ -43,20 +49,24 @@
}
}
- public static int $opt$noinline$testProfile() {
- if (doThrow) throw new Error();
+ static void testAddMethodToProfile(File file, Method m) {
// Make sure we have a profile info for this method without the need to loop.
- return ensureProfilingInfo("$opt$noinline$testProfile");
+ ensureProfilingInfo(m);
+ // Make sure the profile gets saved.
+ ensureProfileProcessing();
+ // Verify that the profile was saved and contains the method.
+ if (!presentInProfile(file.getPath(), m)) {
+ throw new RuntimeException("Method with index " + m + " not in the profile");
+ }
}
- // Return the dex method index.
- public static native int ensureProfilingInfo(String methodName);
+ // Ensure a method has a profiling info.
+ public static native void ensureProfilingInfo(Method method);
// Ensures the profile saver does its usual processing.
public static native void ensureProfileProcessing();
// Checks if the profiles saver knows about the method.
- public static native boolean presentInProfile(String profile, int methodIdx);
+ public static native boolean presentInProfile(String profile, Method method);
- public static boolean doThrow = false;
private static final String TEMP_FILE_NAME_PREFIX = "dummy";
private static final String TEMP_FILE_NAME_SUFFIX = "-file";