summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jiakai Zhang <jiakaiz@google.com> 2023-12-13 18:18:07 +0000
committer Jiakai Zhang <jiakaiz@google.com> 2023-12-18 15:42:03 +0000
commit5e38ea5e680490f40c76faa348d8b979882b3261 (patch)
tree81d3b3f5810574809ea02c96a768b22bec2a30c4
parent5e3fa68e4e650f2961de692fa38d1d488c97d240 (diff)
Make profile inline cache threshold configurable.
Before the change, the threshold was the same as the JIT threshold, which is 65535 by default. After the change, the threshold is unchanged but is configurable through a commandline flag "-Xps-inline-cache-threshold". Bug: 309434431 Test: art/test.py -b --host -r -t 2271 Test: m test-art-host-gtest-art_cmdline_tests Test: Set "dalvik.vm.ps-inline-cache-threshold" to different values, run a compose app benchmark, dump the profile, and see the difference in the profiles. Change-Id: I50a51cf7821b734b497d3a2e56261c5dfc10ef47
-rw-r--r--cmdline/cmdline_parser_test.cc3
-rw-r--r--cmdline/cmdline_types.h17
-rw-r--r--runtime/jit/jit_code_cache.cc13
-rw-r--r--runtime/jit/jit_code_cache.h7
-rw-r--r--runtime/jit/profile_saver.cc3
-rw-r--r--runtime/jit/profile_saver_options.h10
-rw-r--r--runtime/parsed_options.cc1
-rw-r--r--test/2271-profile-inline-cache/expected-stderr.txt0
-rw-r--r--test/2271-profile-inline-cache/expected-stdout.txt1
-rw-r--r--test/2271-profile-inline-cache/info.txt1
-rw-r--r--test/2271-profile-inline-cache/run.py30
-rw-r--r--test/2271-profile-inline-cache/src/Main.java181
-rw-r--r--test/595-profile-saving/profile-saving.cc49
-rw-r--r--test/common/runtime_state.cc83
-rw-r--r--test/knownfailures.json13
15 files changed, 351 insertions, 61 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index e23b0bd8f0..93218bfe32 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -487,7 +487,7 @@ TEST_F(CmdlineParserTest, TestJitOptions) {
* -Xps-*
*/
TEST_F(CmdlineParserTest, ProfileSaverOptions) {
- ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, 8, "abc", true);
+ ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, 8, 9, "abc", true);
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xjitsaveprofilinginfo "
@@ -499,6 +499,7 @@ TEST_F(CmdlineParserTest, ProfileSaverOptions) {
"-Xps-min-classes-to-save:6 "
"-Xps-min-notification-before-wake:7 "
"-Xps-max-notification-before-wake:8 "
+ "-Xps-inline-cache-threshold:9 "
"-Xps-profile-path:abc "
"-Xps-profile-boot-class-path",
M::ProfileSaverOpts);
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index b16f0697fd..7cacfde12a 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -18,6 +18,7 @@
#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
+#include <cstdint>
#include <list>
#include <ostream>
@@ -258,6 +259,17 @@ struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
};
template <>
+struct CmdlineType<uint16_t> : CmdlineTypeParser<uint16_t> {
+ Result Parse(const std::string& str) {
+ return ParseNumeric<uint16_t>(str);
+ }
+
+ static const char* Name() { return "unsigned 16-bit integer"; }
+ static const char* DescribeType() { return "unsigned 16-bit integer value"; }
+};
+
+
+template <>
struct CmdlineType<int> : CmdlineTypeParser<int> {
Result Parse(const std::string& str) {
return ParseNumeric<int>(str);
@@ -849,6 +861,11 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions>
&ProfileSaverOptions::max_notification_before_wake_,
type_parser.Parse(suffix));
}
+ if (android::base::StartsWith(option, "inline-cache-threshold:")) {
+ CmdlineType<uint16_t> type_parser;
+ return ParseInto(
+ existing, &ProfileSaverOptions::inline_cache_threshold_, type_parser.Parse(suffix));
+ }
if (android::base::StartsWith(option, "profile-path:")) {
existing.profile_path_ = suffix;
return Result::SuccessNoValue();
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4f59930806..4e3cf40ac3 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1476,7 +1476,8 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) {
}
void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations,
- std::vector<ProfileMethodInfo>& methods) {
+ std::vector<ProfileMethodInfo>& methods,
+ uint16_t inline_cache_threshold) {
Thread* self = Thread::Current();
WaitUntilInlineCacheAccessible(self);
// TODO: Avoid read barriers for potentially dead methods.
@@ -1495,13 +1496,17 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca
}
std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
- // If the method is still baseline compiled, don't save the inline caches.
- // They might be incomplete and cause unnecessary deoptimizations.
+ // If the method is still baseline compiled and doesn't meet the inline cache threshold, don't
+ // save the inline caches because they might be incomplete.
+ // Although we don't deoptimize for incomplete inline caches in AOT-compiled code, inlining
+ // leads to larger generated code.
// If the inline cache is empty the compiler will generate a regular invoke virtual/interface.
const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
if (ContainsPc(entry_point) &&
CodeInfo::IsBaseline(
- OatQuickMethodHeader::FromEntryPoint(entry_point)->GetOptimizedCodeInfoPtr())) {
+ OatQuickMethodHeader::FromEntryPoint(entry_point)->GetOptimizedCodeInfoPtr()) &&
+ (ProfilingInfo::GetOptimizeThreshold() - info->GetBaselineHotnessCount()) <
+ inline_cache_threshold) {
methods.emplace_back(/*ProfileMethodInfo*/
MethodReference(dex_file, method->GetDexMethodIndex()), inline_caches);
continue;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 82510fbe3c..956687af16 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_JIT_JIT_CODE_CACHE_H_
#define ART_RUNTIME_JIT_JIT_CODE_CACHE_H_
+#include <cstdint>
#include <iosfwd>
#include <memory>
#include <set>
@@ -337,9 +338,11 @@ class JitCodeCache {
void* MoreCore(const void* mspace, intptr_t increment);
// Adds to `methods` all profiled methods which are part of any of the given dex locations.
+ // Saves inline caches for a method if its hotness meets `inline_cache_threshold` after being
+ // baseline compiled.
void GetProfiledMethods(const std::set<std::string>& dex_base_locations,
- std::vector<ProfileMethodInfo>& methods)
- REQUIRES(!Locks::jit_lock_)
+ std::vector<ProfileMethodInfo>& methods,
+ uint16_t inline_cache_threshold) REQUIRES(!Locks::jit_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
void InvalidateAllCompiledCode()
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 125294655a..af5f561d8c 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -868,7 +868,8 @@ bool ProfileSaver::ProcessProfilingInfo(
std::vector<ProfileMethodInfo> profile_methods;
{
ScopedObjectAccess soa(Thread::Current());
- jit_code_cache_->GetProfiledMethods(locations, profile_methods);
+ jit_code_cache_->GetProfiledMethods(
+ locations, profile_methods, options_.GetInlineCacheThreshold());
total_number_of_code_cache_queries_++;
}
{
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index 749205452a..a89d295155 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -13,6 +13,7 @@
#ifndef ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
#define ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
+#include <cstdint>
#include <ostream>
#include <string>
@@ -33,6 +34,7 @@ struct ProfileSaverOptions {
static constexpr uint32_t kMinNotificationBeforeWake = 10;
static constexpr uint32_t kMaxNotificationBeforeWake = 50;
static constexpr uint32_t kHotStartupMethodSamplesNotSet = std::numeric_limits<uint32_t>::max();
+ static constexpr uint16_t kInlineCacheThreshold = std::numeric_limits<uint16_t>::max();
ProfileSaverOptions() :
enabled_(false),
@@ -44,6 +46,7 @@ struct ProfileSaverOptions {
min_classes_to_save_(kMinClassesToSave),
min_notification_before_wake_(kMinNotificationBeforeWake),
max_notification_before_wake_(kMaxNotificationBeforeWake),
+ inline_cache_threshold_(kInlineCacheThreshold),
profile_path_(""),
profile_boot_class_path_(false),
profile_aot_code_(false),
@@ -59,6 +62,7 @@ struct ProfileSaverOptions {
uint32_t min_classes_to_save,
uint32_t min_notification_before_wake,
uint32_t max_notification_before_wake,
+ uint16_t inline_cache_threshold,
const std::string& profile_path,
bool profile_boot_class_path,
bool profile_aot_code = false,
@@ -72,6 +76,7 @@ struct ProfileSaverOptions {
min_classes_to_save_(min_classes_to_save),
min_notification_before_wake_(min_notification_before_wake),
max_notification_before_wake_(max_notification_before_wake),
+ inline_cache_threshold_(inline_cache_threshold),
profile_path_(profile_path),
profile_boot_class_path_(profile_boot_class_path),
profile_aot_code_(profile_aot_code),
@@ -112,6 +117,9 @@ struct ProfileSaverOptions {
uint32_t GetMaxNotificationBeforeWake() const {
return max_notification_before_wake_;
}
+ uint16_t GetInlineCacheThreshold() const {
+ return inline_cache_threshold_;
+ }
std::string GetProfilePath() const {
return profile_path_;
}
@@ -138,6 +146,7 @@ struct ProfileSaverOptions {
<< ", 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_
+ << ", inline_cache_threshold_" << pso.inline_cache_threshold_
<< ", profile_boot_class_path_" << pso.profile_boot_class_path_
<< ", profile_aot_code_" << pso.profile_aot_code_
<< ", wait_for_jit_notifications_to_save_" << pso.wait_for_jit_notifications_to_save_;
@@ -155,6 +164,7 @@ struct ProfileSaverOptions {
uint32_t min_classes_to_save_;
uint32_t min_notification_before_wake_;
uint32_t max_notification_before_wake_;
+ uint16_t inline_cache_threshold_;
std::string profile_path_;
bool profile_boot_class_path_;
bool profile_aot_code_;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 140503fd36..5f350bf750 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -313,6 +313,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
"-Xps-min-classes-to-save:_",
"-Xps-min-notification-before-wake:_",
"-Xps-max-notification-before-wake:_",
+ "-Xps-inline-cache-threshold:_",
"-Xps-profile-path:_"})
.WithHelp("profile-saver options -Xps-<key>:<value>")
.WithType<ProfileSaverOptions>()
diff --git a/test/2271-profile-inline-cache/expected-stderr.txt b/test/2271-profile-inline-cache/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/2271-profile-inline-cache/expected-stderr.txt
diff --git a/test/2271-profile-inline-cache/expected-stdout.txt b/test/2271-profile-inline-cache/expected-stdout.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/2271-profile-inline-cache/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/2271-profile-inline-cache/info.txt b/test/2271-profile-inline-cache/info.txt
new file mode 100644
index 0000000000..4867c4b478
--- /dev/null
+++ b/test/2271-profile-inline-cache/info.txt
@@ -0,0 +1 @@
+Check that inline caches are saved in the profile.
diff --git a/test/2271-profile-inline-cache/run.py b/test/2271-profile-inline-cache/run.py
new file mode 100644
index 0000000000..e77119ee4c
--- /dev/null
+++ b/test/2271-profile-inline-cache/run.py
@@ -0,0 +1,30 @@
+#
+# Copyright 2023 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.
+
+
+def run(ctx, args):
+ # Use
+ # --compiler-filter=verify to make sure that the test is not compiled AOT
+ # and to make sure the test is not compiled when loaded (by PathClassLoader)
+ # -Xjitsaveprofilinginfo to enable profile saving
+ # -Xjitinitialsize:32M to prevent profiling info creation failure.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=verify"],
+ runtime_option=[
+ "-Xjitinitialsize:32M",
+ "-Xjitsaveprofilinginfo",
+ "-Xps-inline-cache-threshold:3000",
+ ])
diff --git a/test/2271-profile-inline-cache/src/Main.java b/test/2271-profile-inline-cache/src/Main.java
new file mode 100644
index 0000000000..365875d05c
--- /dev/null
+++ b/test/2271-profile-inline-cache/src/Main.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ if (!hasJit()) {
+ // Test requires JIT for creating profiling infos.
+ return;
+ }
+
+ File file = null;
+ try {
+ file = createTempFile();
+ String codePath = System.getenv("DEX_LOCATION") + "/2271-profile-inline-cache.jar";
+ VMRuntime.registerAppInfo("test.app", file.getPath(), file.getPath(),
+ new String[] {codePath}, VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
+
+ Derived1 derived1 = new Derived1();
+ Derived2 derived2 = new Derived2();
+
+ // This method is below the inline cache threshold.
+ Method method1 = Main.class.getDeclaredMethod("$noinline$method1", Base.class);
+ ensureJitBaselineCompiled(method1);
+ $noinline$method1(derived1);
+ for (int i = 0; i < 2998; i++) {
+ $noinline$method1(derived2);
+ }
+ checkMethodHasNoInlineCache(file, method1);
+
+ // This method is right on the inline cache threshold.
+ Method method2 = Main.class.getDeclaredMethod("$noinline$method2", Base.class);
+ ensureJitBaselineCompiled(method2);
+ $noinline$method2(derived1);
+ for (int i = 0; i < 2999; i++) {
+ $noinline$method2(derived2);
+ }
+ checkMethodHasInlineCache(file, method2, Derived1.class, Derived2.class);
+
+ // This method is above the inline cache threshold.
+ Method method3 = Main.class.getDeclaredMethod("$noinline$method3", Base.class);
+ ensureJitBaselineCompiled(method3);
+ for (int i = 0; i < 10000; i++) {
+ $noinline$method3(derived1);
+ }
+ for (int i = 0; i < 10000; i++) {
+ $noinline$method3(derived2);
+ }
+ checkMethodHasInlineCache(file, method3, Derived1.class, Derived2.class);
+
+ // This method is above the JIT threshold.
+ Method method4 = Main.class.getDeclaredMethod("$noinline$method4", Base.class);
+ ensureJitBaselineCompiled(method4);
+ $noinline$method4(derived1);
+ $noinline$method4(derived2);
+ ensureMethodJitCompiled(method4);
+ checkMethodHasInlineCache(file, method4, Derived1.class, Derived2.class);
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+ }
+
+ public static void $noinline$method1(Base obj) {
+ obj.f();
+ }
+
+ public static void $noinline$method2(Base obj) {
+ obj.f();
+ }
+
+ public static void $noinline$method3(Base obj) {
+ obj.f();
+ }
+
+ public static void $noinline$method4(Base obj) {
+ obj.f();
+ }
+
+ public static class Base {
+ public void f() {}
+ }
+
+ public static class Derived1 extends Base {
+ @Override
+ public void f() {}
+ }
+
+ public static class Derived2 extends Base {
+ @Override
+ public void f() {}
+ }
+
+ private static void checkMethodHasInlineCache(File file, Method m, Class<?>... targetTypes) {
+ ensureProfileProcessing();
+ if (!hasInlineCacheInProfile(file.getPath(), m, targetTypes)) {
+ throw new RuntimeException("Expected method " + m
+ + " to have inline cache in the profile with target types "
+ + Arrays.stream(targetTypes)
+ .map(Class::getName)
+ .collect(Collectors.joining(", ")));
+ }
+ }
+
+ private static void checkMethodHasNoInlineCache(File file, Method m) {
+ ensureProfileProcessing();
+ if (hasInlineCacheInProfile(file.getPath(), m)) {
+ throw new RuntimeException(
+ "Expected method " + m + " not to have inline cache in the profile");
+ }
+ }
+
+ public static void ensureJitBaselineCompiled(Method method) {
+ ensureJitBaselineCompiled(method.getDeclaringClass(), method.getName());
+ }
+ public static native void ensureMethodJitCompiled(Method method);
+ public static native void ensureJitBaselineCompiled(Class<?> cls, String methodName);
+ public static native void ensureProfileProcessing();
+ public static native boolean hasInlineCacheInProfile(
+ String profile, Method method, Class<?>... targetTypes);
+ public static native boolean hasJit();
+
+ private static final String TEMP_FILE_NAME_PREFIX = "temp";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+
+ private static class VMRuntime {
+ public static final int CODE_PATH_TYPE_PRIMARY_APK = 1 << 0;
+ private static final Method registerAppInfoMethod;
+
+ static {
+ try {
+ Class<? extends Object> c = Class.forName("dalvik.system.VMRuntime");
+ registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo", String.class,
+ String.class, String.class, String[].class, int.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String packageName, String curProfile, String refProfile,
+ String[] codePaths, int codePathsType) throws Exception {
+ registerAppInfoMethod.invoke(
+ null, packageName, curProfile, refProfile, codePaths, codePathsType);
+ }
+ }
+}
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index bec4ae923f..65239edd19 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -14,18 +14,11 @@
* limitations under the License.
*/
-#include "dex/dex_file.h"
-
#include "art_method-inl.h"
-#include "dex/method_reference.h"
-#include "jit/profile_saver.h"
+#include "art_method.h"
+#include "jit/profiling_info.h"
#include "jni.h"
-#include "mirror/class-inl.h"
#include "mirror/executable.h"
-#include "nativehelper/ScopedUtfChars.h"
-#include "oat_file_assistant.h"
-#include "oat_file_manager.h"
-#include "profile/profile_compilation_info.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
@@ -44,43 +37,5 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
}
}
-extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
- ProfileSaver::ForceProcessProfiles();
-}
-
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isForBootImage(JNIEnv* env,
- jclass,
- jstring filename) {
- ScopedUtfChars filename_chars(env, filename);
- CHECK(filename_chars.c_str() != nullptr);
-
- ProfileCompilationInfo info(/*for_boot_image=*/ true);
- bool result = info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/ false);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
- jclass c,
- jstring filename,
- jobject method) {
- bool for_boot_image = Java_Main_isForBootImage(env, c, filename) == JNI_TRUE;
- ScopedUtfChars filename_chars(env, filename);
- CHECK(filename_chars.c_str() != nullptr);
- ScopedObjectAccess soa(env);
- ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
- ArtMethod* art_method = exec->GetArtMethod();
- MethodReference ref(art_method->GetDexFile(), art_method->GetDexMethodIndex());
-
- ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(), for_boot_image);
- if (!info.Load(filename_chars.c_str(), /*clear_if_invalid=*/false)) {
- LOG(ERROR) << "Failed to load profile from " << filename;
- return JNI_FALSE;
- }
- const ProfileCompilationInfo::MethodHotness hotness = info.GetMethodHotness(ref);
- // TODO: Why do we check `hotness.IsHot()` instead of `hotness.IsInProfile()`
- // in a method named `presentInProfile()`?
- return hotness.IsHot() ? JNI_TRUE : JNI_FALSE;
-}
-
} // namespace
} // namespace art
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index c1c8936e9e..27f1ebad12 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -23,15 +23,18 @@
#include "base/enums.h"
#include "common_throws.h"
#include "dex/dex_file-inl.h"
+#include "dex/dex_file_types.h"
#include "gc/heap.h"
#include "instrumentation.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "jit/profile_saver.h"
#include "jit/profiling_info.h"
#include "jni.h"
#include "jni/jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
+#include "mirror/executable.h"
#include "nativehelper/ScopedUtfChars.h"
#include "oat.h"
#include "oat_file.h"
@@ -51,6 +54,7 @@ static jit::Jit* GetJitIfEnabled() {
bool can_jit =
runtime != nullptr
&& runtime->GetJit() != nullptr
+ && runtime->UseJitCompilation()
&& runtime->GetInstrumentation()->GetCurrentInstrumentationLevel() !=
instrumentation::Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter;
return can_jit ? runtime->GetJit() : nullptr;
@@ -400,7 +404,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) {
std::set<std::string> unused_locations;
unused_locations.insert("fake_location");
ScopedObjectAccess soa(Thread::Current());
- code_cache->GetProfiledMethods(unused_locations, unused_vector);
+ code_cache->GetProfiledMethods(unused_locations, unused_vector, /*inline_cache_threshold=*/0);
}
extern "C" JNIEXPORT void JNICALL Java_Main_waitForCompilation(JNIEnv*, jclass) {
@@ -485,4 +489,81 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInImageSpace(JNIEnv* env,
return space->IsImageSpace() ? JNI_TRUE : JNI_FALSE;
}
+// Ensures the profile saver does its usual processing.
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
+ ProfileSaver::ForceProcessProfiles();
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isForBootImage(JNIEnv* env,
+ jclass,
+ jstring filename) {
+ ScopedUtfChars filename_chars(env, filename);
+ CHECK(filename_chars.c_str() != nullptr);
+
+ ProfileCompilationInfo info(/*for_boot_image=*/true);
+ bool result = info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/false);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static ProfileCompilationInfo::MethodHotness GetMethodHotnessFromProfile(JNIEnv* env,
+ jclass c,
+ jstring filename,
+ jobject method) {
+ bool for_boot_image = Java_Main_isForBootImage(env, c, filename) == JNI_TRUE;
+ ScopedUtfChars filename_chars(env, filename);
+ CHECK(filename_chars.c_str() != nullptr);
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+ ArtMethod* art_method = exec->GetArtMethod();
+ MethodReference ref(art_method->GetDexFile(), art_method->GetDexMethodIndex());
+
+ ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(), for_boot_image);
+ if (!info.Load(filename_chars.c_str(), /*clear_if_invalid=*/false)) {
+ LOG(ERROR) << "Failed to load profile from " << filename;
+ return ProfileCompilationInfo::MethodHotness();
+ }
+ return info.GetMethodHotness(ref);
+}
+
+// Checks if the method is present in the profile.
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
+ jclass c,
+ jstring filename,
+ jobject method) {
+ // TODO: Why do we check `hotness.IsHot()` instead of `hotness.IsInProfile()`
+ // in a method named `presentInProfile()`?
+ return GetMethodHotnessFromProfile(env, c, filename, method).IsHot() ? JNI_TRUE : JNI_FALSE;
+}
+
+// Checks if the method has an inline cache in the profile that contains at least the given target
+// types.
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasInlineCacheInProfile(
+ JNIEnv* env, jclass c, jstring filename, jobject method, jobjectArray target_types) {
+ ProfileCompilationInfo::MethodHotness hotness =
+ GetMethodHotnessFromProfile(env, c, filename, method);
+ if (hotness.GetInlineCacheMap() == nullptr) {
+ return JNI_FALSE;
+ }
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::ObjectArray<mirror::Class>> types =
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(target_types);
+ for (const auto& [dex_pc, dex_pc_data] : *hotness.GetInlineCacheMap()) {
+ bool match = true;
+ for (ObjPtr<mirror::Class> type : *types.Ptr()) {
+ dex::TypeIndex expected_index = type->GetDexTypeIndex();
+ if (!expected_index.IsValid()) {
+ return JNI_FALSE;
+ }
+ if (dex_pc_data.classes.find(expected_index) == dex_pc_data.classes.end()) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return JNI_TRUE;
+ }
+ }
+ return JNI_FALSE;
+}
+
} // namespace art
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 659e719e51..0ef90de38f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -341,8 +341,9 @@
},
{
"tests": ["000-nop",
- "595-profile-saving"],
- "description": "The doesn't compile anything",
+ "595-profile-saving",
+ "2271-profile-inline-cache"],
+ "description": "The test doesn't compile anything",
"env_vars": {"ART_TEST_BISECTION": "true"},
"variant": "optimizing"
},
@@ -515,7 +516,8 @@
"924-threads",
"981-dedup-original-dex",
"1900-track-alloc",
- "2230-profile-save-hotness"
+ "2230-profile-save-hotness",
+ "2271-profile-inline-cache"
],
"description": ["Tests that require exact knowledge of the deoptimization state, the ",
"number of plugins and agents, or breaks other openjdkjvmti assumptions."],
@@ -950,7 +952,6 @@
"593-checker-shift-and-simplifier",
"594-invoke-super",
"595-error-class",
- "595-profile-saving",
"596-app-images",
"596-checker-dead-phi",
"596-monitor-inflation",
@@ -1152,6 +1153,7 @@
{
"tests": ["178-app-image-native-method",
"530-checker-peel-unroll",
+ "595-profile-saving",
"616-cha-unloading",
"674-hiddenapi",
"677-fsi2",
@@ -1244,7 +1246,8 @@
"2254-class-value-before-and-after-u",
"2261-badcleaner-in-systemcleaner",
"2263-method-trace-jit",
- "2270-mh-internal-hiddenapi-use"],
+ "2270-mh-internal-hiddenapi-use",
+ "2271-profile-inline-cache"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},