summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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."]
},