diff options
| -rw-r--r-- | cmdline/cmdline_parser_test.cc | 3 | ||||
| -rw-r--r-- | cmdline/cmdline_types.h | 17 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 13 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.h | 7 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.cc | 3 | ||||
| -rw-r--r-- | runtime/jit/profile_saver_options.h | 10 | ||||
| -rw-r--r-- | runtime/parsed_options.cc | 1 | ||||
| -rw-r--r-- | test/2271-profile-inline-cache/expected-stderr.txt | 0 | ||||
| -rw-r--r-- | test/2271-profile-inline-cache/expected-stdout.txt | 1 | ||||
| -rw-r--r-- | test/2271-profile-inline-cache/info.txt | 1 | ||||
| -rw-r--r-- | test/2271-profile-inline-cache/run.py | 30 | ||||
| -rw-r--r-- | test/2271-profile-inline-cache/src/Main.java | 181 | ||||
| -rw-r--r-- | test/595-profile-saving/profile-saving.cc | 49 | ||||
| -rw-r--r-- | test/common/runtime_state.cc | 83 | ||||
| -rw-r--r-- | test/knownfailures.json | 13 |
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."] }, |