summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmdline/cmdline_parser_test.cc14
-rw-r--r--cmdline/cmdline_types.h6
-rw-r--r--compiler/jit/jit_compiler.cc3
-rw-r--r--compiler/optimizing/nodes.h2
-rw-r--r--compiler/optimizing/optimizing_compiler.cc6
-rw-r--r--runtime/art_method-inl.h7
-rw-r--r--runtime/art_method.h1
-rw-r--r--runtime/interpreter/mterp/nterp.cc3
-rw-r--r--runtime/jit/jit_code_cache.cc33
-rw-r--r--runtime/jit/jit_code_cache.h1
-rw-r--r--runtime/jit/profile_saver.cc32
-rw-r--r--runtime/jit/profile_saver_options.h18
-rw-r--r--runtime/jit/profiling_info.h8
-rw-r--r--test/2230-profile-save-hotness/run.py5
-rw-r--r--test/2230-profile-save-hotness/src-art/Main.java4
15 files changed, 111 insertions, 32 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index e586dd4292..5d6ba4446e 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -38,6 +38,7 @@ namespace art {
return lhs.enabled_ == rhs.enabled_ &&
lhs.min_save_period_ms_ == rhs.min_save_period_ms_ &&
lhs.save_resolved_classes_delay_ms_ == rhs.save_resolved_classes_delay_ms_ &&
+ lhs.hot_startup_method_samples_ == rhs.hot_startup_method_samples_ &&
lhs.min_methods_to_save_ == rhs.min_methods_to_save_ &&
lhs.min_classes_to_save_ == rhs.min_classes_to_save_ &&
lhs.min_notification_before_wake_ == rhs.min_notification_before_wake_ &&
@@ -489,18 +490,19 @@ 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 "
"-Xps-min-save-period-ms:1 "
"-Xps-min-first-save-ms:2 "
"-Xps-save-resolved-classes-delay-ms:3 "
- "-Xps-min-methods-to-save:4 "
- "-Xps-min-classes-to-save:5 "
- "-Xps-min-notification-before-wake:6 "
- "-Xps-max-notification-before-wake:7 "
- "-Xps-inline-cache-threshold:8 "
+ "-Xps-hot-startup-method-samples:4 "
+ "-Xps-min-methods-to-save:5 "
+ "-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 34c1b0fc97..7cacfde12a 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -832,8 +832,10 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions>
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "hot-startup-method-samples:")) {
- LOG(WARNING) << "-Xps-hot-startup-method-samples option is deprecated";
- return Result::SuccessNoValue();
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::hot_startup_method_samples_,
+ type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "min-methods-to-save:")) {
CmdlineType<unsigned int> type_parser;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 4b2f8d2e14..051368cc8a 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -178,8 +178,7 @@ bool JitCompiler::CompileMethod(
Thread* self, JitMemoryRegion* region, ArtMethod* method, CompilationKind compilation_kind) {
SCOPED_TRACE << "JIT compiling "
<< method->PrettyMethod()
- << " (kind=" << compilation_kind << ")"
- << " from " << method->GetDexFile()->GetLocation();
+ << " (kind=" << compilation_kind << ")";
DCHECK(!method->IsProxyMethod());
DCHECK(method->GetDeclaringClass()->IsResolved());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 89369f59f1..33ffc07ba8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -143,8 +143,6 @@ enum GraphAnalysisResult {
kAnalysisSuccess,
};
-std::ostream& operator<<(std::ostream& os, GraphAnalysisResult ga);
-
template <typename T>
static inline typename std::make_unsigned<T>::type MakeUnsigned(T x) {
return static_cast<typename std::make_unsigned<T>::type>(x);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3f73459a00..45d534a9ec 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -30,7 +30,6 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "base/scoped_arena_allocator.h"
-#include "base/systrace.h"
#include "base/timing_logger.h"
#include "builder.h"
#include "code_generator.h"
@@ -782,7 +781,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator,
}
if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
- SCOPED_TRACE << "Not compiling because of pathological case";
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledPathological);
return nullptr;
}
@@ -793,7 +791,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator,
if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace)
&& (CodeItemInstructionAccessor(dex_file, code_item).InsnsSizeInCodeUnits() >
kSpaceFilterOptimizingThreshold)) {
- SCOPED_TRACE << "Not compiling because of space filter";
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter);
return nullptr;
}
@@ -868,7 +865,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator,
compilation_stats_.get());
GraphAnalysisResult result = builder.BuildGraph();
if (result != kAnalysisSuccess) {
- SCOPED_TRACE << "Not compiling because of " << result;
switch (result) {
case kAnalysisSkipped: {
MaybeRecordStat(compilation_stats_.get(),
@@ -931,7 +927,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator,
// However, we may have run out of memory trying to create it, so in this
// case just abort the compilation.
if (graph->GetProfilingInfo() == nullptr) {
- SCOPED_TRACE << "Not compiling because of out of memory";
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit);
return nullptr;
}
@@ -943,7 +938,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator,
compilation_stats_.get());
if (UNLIKELY(codegen->GetFrameSize() > codegen->GetMaximumFrameSize())) {
- SCOPED_TRACE << "Not compiling because of stack frame too large";
LOG(WARNING) << "Stack frame size is " << codegen->GetFrameSize()
<< " which is larger than the maximum of " << codegen->GetMaximumFrameSize()
<< " bytes. Method: " << graph->PrettyMethod();
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d87040ab49..b2711d968a 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -777,6 +777,13 @@ inline bool ArtMethod::CounterIsHot() {
return hotness_count_ == 0;
}
+inline bool ArtMethod::CounterHasReached(uint16_t samples, uint16_t threshold) {
+ DCHECK(!IsAbstract());
+ DCHECK_EQ(threshold, Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
+ DCHECK_LE(samples, threshold);
+ return hotness_count_ <= (threshold - samples);
+}
+
inline uint16_t ArtMethod::GetCounter() {
DCHECK(!IsAbstract());
return hotness_count_;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 0a6cda65fd..a23969aa73 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -1011,6 +1011,7 @@ class EXPORT ArtMethod final {
ALWAYS_INLINE void UpdateCounter(int32_t new_samples);
ALWAYS_INLINE void SetHotCounter();
ALWAYS_INLINE bool CounterIsHot();
+ ALWAYS_INLINE bool CounterHasReached(uint16_t samples, uint16_t threshold);
ALWAYS_INLINE uint16_t GetCounter();
ALWAYS_INLINE bool CounterHasChanged(uint16_t threshold);
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index 64f04d6fde..d8ce2bcaed 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -693,10 +693,7 @@ extern "C" jit::OsrData* NterpHotMethod(ArtMethod* method, uint16_t* dex_pc_ptr,
DCHECK_EQ(Thread::Current()->GetSharedMethodHotness(), 0u);
Thread::Current()->ResetSharedMethodHotness();
} else {
- // Move the counter to the initial threshold in case we have to re-JIT it.
method->ResetCounter(runtime->GetJITOptions()->GetWarmupThreshold());
- // Mark the method as warm for the profile saver.
- method->SetPreviouslyWarm();
}
jit::Jit* jit = runtime->GetJit();
if (jit != nullptr && jit->UseJitCompilation()) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4b69dc5c01..89be345875 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -649,6 +649,17 @@ void JitCodeCache::CopyInlineCacheInto(
}
}
+static void ClearMethodCounter(ArtMethod* method, bool was_warm)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (was_warm) {
+ method->SetPreviouslyWarm();
+ }
+ method->ResetCounter(Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
+ // We add one sample so that the profile knows that the method was executed at least once.
+ // This is required for layout purposes.
+ method->UpdateCounter(/* new_samples= */ 1);
+}
+
bool JitCodeCache::Commit(Thread* self,
JitMemoryRegion* region,
ArtMethod* method,
@@ -716,9 +727,10 @@ bool JitCodeCache::Commit(Thread* self,
bool single_impl_still_valid = true;
for (ArtMethod* single_impl : cha_single_implementation_list) {
if (!single_impl->HasSingleImplementation()) {
- // Simply discard the compiled code.
+ // Simply discard the compiled code. Clear the counter so that it may be recompiled later.
// Hopefully the class hierarchy will be more stable when compilation is retried.
single_impl_still_valid = false;
+ ClearMethodCounter(method, /*was_warm=*/ false);
break;
}
}
@@ -813,6 +825,7 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) {
return false;
}
+ ClearMethodCounter(method, /* was_warm= */ false);
Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
VLOG(jit)
<< "JIT removed (osr=" << std::boolalpha << osr << std::noboolalpha << ") "
@@ -1234,6 +1247,15 @@ void JitCodeCache::MaybeUpdateInlineCache(ArtMethod* method,
info->AddInvokeInfo(dex_pc, cls.Ptr());
}
+void JitCodeCache::ResetHotnessCounter(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
+ auto it = profiling_infos_.find(method);
+ DCHECK(it != profiling_infos_.end());
+ it->second->ResetCounter();
+}
+
+
void JitCodeCache::DoCollection(Thread* self) {
ScopedTrace trace(__FUNCTION__);
@@ -1584,6 +1606,8 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method,
VLOG(jit) << "Not compiling "
<< method->PrettyMethod()
<< " because it has the resolution stub";
+ // Give it a new chance to be hot.
+ ClearMethodCounter(method, /*was_warm=*/ false);
return false;
}
}
@@ -1684,12 +1708,15 @@ void JitCodeCache::InvalidateAllCompiledCode() {
OatQuickMethodHeader::FromCodePointer(data.GetCode());
for (ArtMethod* method : data.GetMethods()) {
if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) {
+ ClearMethodCounter(method, /*was_warm=*/true);
instr->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
}
}
}
for (const auto& entry : method_code_map_) {
ArtMethod* meth = entry.second;
+ // We were compiled, so we must be warm.
+ ClearMethodCounter(meth, /*was_warm=*/true);
if (UNLIKELY(meth->IsObsolete())) {
linker->SetEntryPointsForObsoleteMethod(meth);
} else {
@@ -1719,8 +1746,10 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method,
// Clear the method counter if we are running jitted code since we might want to jit this again in
// the future.
if (method_entrypoint == header->GetEntryPoint()) {
- // The entrypoint is the one to invalidate, so we just update it to the interpreter entry point.
+ // The entrypoint is the one to invalidate, so we just update it to the interpreter entry point
+ // and clear the counter to get the method Jitted again.
Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
+ ClearMethodCounter(method, /*was_warm=*/ true);
} else {
Thread* self = Thread::Current();
ScopedDebugDisallowReadBarriers sddrb(self);
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 3dd57121ca..7de29d4024 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -414,6 +414,7 @@ class JitCodeCache {
}
ProfilingInfo* GetProfilingInfo(ArtMethod* method, Thread* self);
+ void ResetHotnessCounter(ArtMethod* method, Thread* self);
void MaybeUpdateInlineCache(ArtMethod* method,
uint32_t dex_pc,
ObjPtr<mirror::Class> cls,
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 3506faaa7f..abf01f5498 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -335,6 +335,7 @@ class ProfileSaver::GetClassesAndMethodsHelper {
REQUIRES_SHARED(Locks::mutator_lock_)
: startup_(startup),
profile_boot_class_path_(options.GetProfileBootClassPath()),
+ hot_method_sample_threshold_(CalculateHotMethodSampleThreshold(startup, options)),
extra_flags_(GetExtraMethodHotnessFlags(options)),
annotation_(annotation),
arena_stack_(Runtime::Current()->GetArenaPool()),
@@ -357,6 +358,10 @@ class ProfileSaver::GetClassesAndMethodsHelper {
void CollectClasses(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
void UpdateProfile(const std::set<std::string>& locations, ProfileCompilationInfo* profile_info);
+ uint32_t GetHotMethodSampleThreshold() const {
+ return hot_method_sample_threshold_;
+ }
+
size_t GetNumberOfHotMethods() const {
return number_of_hot_methods_;
}
@@ -404,6 +409,19 @@ class ProfileSaver::GetClassesAndMethodsHelper {
using DexFileRecordsMap = ScopedArenaHashMap<const DexFile*, DexFileRecords*>;
+ static uint32_t CalculateHotMethodSampleThreshold(bool startup,
+ const ProfileSaverOptions& options) {
+ Runtime* runtime = Runtime::Current();
+ if (startup) {
+ const bool is_low_ram = runtime->GetHeap()->IsLowMemoryMode();
+ return options.GetHotStartupMethodSamples(is_low_ram);
+ } else if (runtime->GetJit() != nullptr) {
+ return runtime->GetJit()->WarmMethodThreshold();
+ } else {
+ return std::numeric_limits<uint32_t>::max();
+ }
+ }
+
ALWAYS_INLINE static bool ShouldCollectClasses(bool startup) {
// We only record classes for the startup case. This may change in the future.
return startup;
@@ -416,6 +434,7 @@ class ProfileSaver::GetClassesAndMethodsHelper {
const bool startup_;
const bool profile_boot_class_path_;
+ const uint32_t hot_method_sample_threshold_;
const uint32_t extra_flags_;
const ProfileCompilationInfo::ProfileSampleAnnotation annotation_;
ArenaStack arena_stack_;
@@ -590,6 +609,7 @@ void ProfileSaver::GetClassesAndMethodsHelper::UpdateProfile(const std::set<std:
ProfileCompilationInfo* profile_info) {
// Move members to local variables to allow the compiler to optimize this properly.
const bool startup = startup_;
+ const uint32_t hot_method_sample_threshold = hot_method_sample_threshold_;
const uint32_t base_flags =
(startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup) | extra_flags_;
@@ -599,9 +619,10 @@ void ProfileSaver::GetClassesAndMethodsHelper::UpdateProfile(const std::set<std:
uint16_t initial_value = Runtime::Current()->GetJITOptions()->GetWarmupThreshold();
auto get_method_flags = [&](ArtMethod& method) {
- // Mark methods as hot if they are marked as such (warm for the runtime
- // means hot for the profile).
- if (method.PreviouslyWarm()) {
+ // Mark methods as hot if they have more than hot_method_sample_threshold
+ // samples. This means they will get compiled by the compiler driver.
+ if (method.PreviouslyWarm() ||
+ method.CounterHasReached(hot_method_sample_threshold, initial_value)) {
++number_of_hot_methods;
return enum_cast<ProfileCompilationInfo::MethodHotness::Flag>(base_flags | Hotness::kFlagHot);
} else if (method.CounterHasChanged(initial_value)) {
@@ -729,6 +750,7 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
profiler_pthread = profiler_pthread_;
}
+ uint32_t hot_method_sample_threshold = 0u;
size_t number_of_hot_methods = 0u;
size_t number_of_sampled_methods = 0u;
{
@@ -743,6 +765,7 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
ScopedObjectAccess soa(self);
GetClassesAndMethodsHelper helper(startup, options_, GetProfileSampleAnnotation());
+ hot_method_sample_threshold = helper.GetHotMethodSampleThreshold();
helper.CollectClasses(self);
// Release the mutator lock. We shall need to re-acquire the lock for a moment to
@@ -777,7 +800,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
}
VLOG(profiler) << "Profile saver recorded " << number_of_hot_methods
<< " hot methods and " << number_of_sampled_methods
- << " sampled methods in " << PrettyDuration(NanoTime() - start_time);
+ << " sampled methods with threshold " << hot_method_sample_threshold
+ << " in " << PrettyDuration(NanoTime() - start_time);
}
bool ProfileSaver::ProcessProfilingInfo(
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index ed2f00f48f..f6d928ff6b 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -26,10 +26,14 @@ struct ProfileSaverOptions {
// period is not configured.
static constexpr uint32_t kMinFirstSaveMsNotSet = 0;
static constexpr uint32_t kSaveResolvedClassesDelayMs = 5 * 1000; // 5 seconds
+ // Minimum number of JIT samples during launch to mark a method as hot in the profile.
+ static constexpr uint32_t kHotStartupMethodSamples = 1;
+ static constexpr uint32_t kHotStartupMethodSamplesLowRam = 256;
static constexpr uint32_t kMinMethodsToSave = 10;
static constexpr uint32_t kMinClassesToSave = 10;
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 = 4000;
ProfileSaverOptions() :
@@ -37,6 +41,7 @@ struct ProfileSaverOptions {
min_save_period_ms_(kMinSavePeriodMs),
min_first_save_ms_(kMinFirstSaveMsNotSet),
save_resolved_classes_delay_ms_(kSaveResolvedClassesDelayMs),
+ hot_startup_method_samples_(kHotStartupMethodSamplesNotSet),
min_methods_to_save_(kMinMethodsToSave),
min_classes_to_save_(kMinClassesToSave),
min_notification_before_wake_(kMinNotificationBeforeWake),
@@ -52,6 +57,7 @@ struct ProfileSaverOptions {
uint32_t min_save_period_ms,
uint32_t min_first_save_ms,
uint32_t save_resolved_classes_delay_ms,
+ uint32_t hot_startup_method_samples,
uint32_t min_methods_to_save,
uint32_t min_classes_to_save,
uint32_t min_notification_before_wake,
@@ -65,6 +71,7 @@ struct ProfileSaverOptions {
min_save_period_ms_(min_save_period_ms),
min_first_save_ms_(min_first_save_ms),
save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
+ hot_startup_method_samples_(hot_startup_method_samples),
min_methods_to_save_(min_methods_to_save),
min_classes_to_save_(min_classes_to_save),
min_notification_before_wake_(min_notification_before_wake),
@@ -91,6 +98,13 @@ struct ProfileSaverOptions {
uint32_t GetSaveResolvedClassesDelayMs() const {
return save_resolved_classes_delay_ms_;
}
+ uint32_t GetHotStartupMethodSamples(bool is_low_ram) const {
+ uint32_t ret = hot_startup_method_samples_;
+ if (ret == kHotStartupMethodSamplesNotSet) {
+ ret = is_low_ram ? kHotStartupMethodSamplesLowRam : kHotStartupMethodSamples;
+ }
+ return ret;
+ }
uint32_t GetMinMethodsToSave() const {
return min_methods_to_save_;
}
@@ -127,6 +141,7 @@ struct ProfileSaverOptions {
<< ", min_save_period_ms_" << pso.min_save_period_ms_
<< ", min_first_save_ms_" << pso.min_first_save_ms_
<< ", save_resolved_classes_delay_ms_" << pso.save_resolved_classes_delay_ms_
+ << ", hot_startup_method_samples_" << pso.hot_startup_method_samples_
<< ", 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_
@@ -142,6 +157,9 @@ struct ProfileSaverOptions {
uint32_t min_save_period_ms_;
uint32_t min_first_save_ms_;
uint32_t save_resolved_classes_delay_ms_;
+ // Do not access hot_startup_method_samples_ directly for reading since it may be set to the
+ // placeholder default.
+ uint32_t hot_startup_method_samples_;
uint32_t min_methods_to_save_;
uint32_t min_classes_to_save_;
uint32_t min_notification_before_wake_;
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index a32b712b0c..ae59d0ade1 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -171,6 +171,14 @@ class ProfilingInfo {
return MemberOffset(OFFSETOF_MEMBER(ProfilingInfo, baseline_hotness_count_));
}
+ void ResetCounter() {
+ baseline_hotness_count_ = GetOptimizeThreshold();
+ }
+
+ bool CounterHasChanged() const {
+ return baseline_hotness_count_ != GetOptimizeThreshold();
+ }
+
uint16_t GetBaselineHotnessCount() const {
return baseline_hotness_count_;
}
diff --git a/test/2230-profile-save-hotness/run.py b/test/2230-profile-save-hotness/run.py
index e2f4473b86..526b841b87 100644
--- a/test/2230-profile-save-hotness/run.py
+++ b/test/2230-profile-save-hotness/run.py
@@ -16,11 +16,10 @@
def run(ctx, args):
ctx.default_run(
args,
- # Profiling is only done on interpreted and JITted code.
Xcompiler_option=[
- "--compiler-filter=verify"
+ "--count-hotness-in-compiled-code", "--compiler-filter=speed"
],
runtime_option=[
- "-Xjitsaveprofilinginfo", "-Xusejit:true"
+ "-Xps-profile-aot-code", "-Xjitsaveprofilinginfo", "-Xusejit:true"
],
)
diff --git a/test/2230-profile-save-hotness/src-art/Main.java b/test/2230-profile-save-hotness/src-art/Main.java
index 06e89b2ade..c9132e6e7d 100644
--- a/test/2230-profile-save-hotness/src-art/Main.java
+++ b/test/2230-profile-save-hotness/src-art/Main.java
@@ -47,7 +47,7 @@ public class Main {
new String[] {codePath},
VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
- // Test that the profile saves an app method that gets JITted.
+ // Test that the profile saves an app method with a profiling info.
$noinline$hotnessCountWithLoop(100000);
ensureProfileProcessing();
Method appMethod = Main.class.getDeclaredMethod(methodName);
@@ -72,7 +72,7 @@ public class Main {
}
}
- // Checks if the profile saver has the method as hot/warm.
+ // Checks if the profiles saver has the method as hot/warm.
public static native boolean presentInProfile(String profile, Method method);
// Ensures the profile saver does its usual processing.
public static native void ensureProfileProcessing();