summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Calin Juravle <calin@google.com> 2016-04-20 14:22:09 +0100
committer Calin Juravle <calin@google.com> 2016-04-28 16:15:04 +0100
commitffc87076dda9878cb2cc098149bae441d38b9268 (patch)
treee587208d6a8e62532792add3e1ace6b4e6d73e0f
parent97cbc9206e9adc473a90650ebdb5d620f517ff04 (diff)
Split profile recording from jit compilation
We still use ProfileInfo objects to record profile information. That gives us the flexibility to add the inline caches in the future and the convenience of the already implemented GC. If UseJIT is false and SaveProfilingInfo true, we will only record the ProfileInfo and never launch compilation tasks. Bug: 27916886 (cherry picked from commit e5de54cfab5f14ba0b8ff25d8d60901c7021943f) Change-Id: I68afc181d71447895fb12346c1806e99bcab1de2
-rw-r--r--cmdline/cmdline_parser_test.cc4
-rw-r--r--compiler/dex/verification_results.cc2
-rw-r--r--compiler/dex/verified_method.cc5
-rw-r--r--compiler/driver/compiler_driver.cc18
-rw-r--r--compiler/optimizing/code_generator_arm.cc4
-rw-r--r--compiler/optimizing/code_generator_arm64.cc4
-rw-r--r--compiler/optimizing/code_generator_x86.cc4
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc4
-rw-r--r--compiler/optimizing/inliner.cc6
-rw-r--r--compiler/optimizing/sharpening.cc10
-rw-r--r--runtime/art_method.cc4
-rw-r--r--runtime/class_linker.cc3
-rw-r--r--runtime/gc/heap.cc4
-rw-r--r--runtime/jit/jit.cc49
-rw-r--r--runtime/jit/jit.h24
-rw-r--r--runtime/jit/profile_saver.cc30
-rw-r--r--runtime/jit/profile_saver.h11
-rw-r--r--runtime/parsed_options.cc2
-rw-r--r--runtime/quick/inline_method_analyser.cc2
-rw-r--r--runtime/quick_exception_handler.cc2
-rw-r--r--runtime/runtime.cc33
-rw-r--r--runtime/runtime.h10
-rw-r--r--runtime/runtime_options.def2
-rw-r--r--runtime/stack.cc4
-rw-r--r--test/570-checker-osr/osr.cc6
-rw-r--r--test/595-profile-saving/expected.txt1
-rw-r--r--test/595-profile-saving/info.txt1
-rw-r--r--test/595-profile-saving/profile-saving.cc83
-rw-r--r--test/595-profile-saving/run28
-rw-r--r--test/595-profile-saving/src/Main.java99
-rw-r--r--test/Android.libarttest.mk3
31 files changed, 379 insertions, 83 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 81b854e9c3..7c53e01c47 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -461,8 +461,8 @@ TEST_F(CmdlineParserTest, TestJitOptions) {
* Test successes
*/
{
- EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJIT);
- EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJIT);
+ EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJitCompilation);
+ EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJitCompilation);
}
{
EXPECT_SINGLE_PARSE_VALUE(
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 1491a183f1..606302bd78 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -60,7 +60,7 @@ void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method
// TODO: Investigate why are we doing the work again for this method and try to avoid it.
LOG(WARNING) << "Method processed more than once: "
<< PrettyMethod(ref.dex_method_index, *ref.dex_file);
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
}
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 5c0253c29e..bace014713 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -54,7 +54,8 @@ const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_ve
}
// Only need dequicken info for JIT so far.
- if (Runtime::Current()->UseJit() && !verified_method->GenerateDequickenMap(method_verifier)) {
+ if (Runtime::Current()->UseJitCompilation() &&
+ !verified_method->GenerateDequickenMap(method_verifier)) {
return nullptr;
}
}
@@ -72,7 +73,7 @@ const MethodReference* VerifiedMethod::GetDevirtTarget(uint32_t dex_pc) const {
}
const DexFileReference* VerifiedMethod::GetDequickenIndex(uint32_t dex_pc) const {
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
auto it = dequicken_map_.find(dex_pc);
return (it != dequicken_map_.end()) ? &it->second : nullptr;
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e84faff941..9c3aceb590 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -473,7 +473,7 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
const DexFile& dex_file, const DexFile::ClassDef& class_def)
SHARED_REQUIRES(Locks::mutator_lock_) {
auto* const runtime = Runtime::Current();
- if (runtime->UseJit() || driver.GetCompilerOptions().VerifyAtRuntime()) {
+ if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) {
// Verify at runtime shouldn't dex to dex since we didn't resolve of verify.
return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
}
@@ -1268,7 +1268,7 @@ void CompilerDriver::UpdateImageClasses(TimingLogger* timings) {
bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) {
Runtime* runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) {
- DCHECK(runtime->UseJit());
+ DCHECK(runtime->UseJitCompilation());
// Having the klass reference here implies that the klass is already loaded.
return true;
}
@@ -1289,7 +1289,7 @@ bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> d
if ((IsBootImage() &&
IsImageClass(dex_cache->GetDexFile()->StringDataByIdx(
dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) ||
- Runtime::Current()->UseJit()) {
+ Runtime::Current()->UseJitCompilation()) {
mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
result = (resolved_class != nullptr);
}
@@ -1307,7 +1307,7 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file,
// See also Compiler::ResolveDexFile
bool result = false;
- if (IsBootImage() || Runtime::Current()->UseJit()) {
+ if (IsBootImage() || Runtime::Current()->UseJitCompilation()) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
@@ -1319,7 +1319,7 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file,
result = true;
} else {
// Just check whether the dex cache already has the string.
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
result = (dex_cache->GetResolvedString(string_idx) != nullptr);
}
}
@@ -1427,7 +1427,7 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i
} else {
return false;
}
- } else if (runtime->UseJit() && !heap->IsMovableObject(resolved_class)) {
+ } else if (runtime->UseJitCompilation() && !heap->IsMovableObject(resolved_class)) {
*is_type_initialized = resolved_class->IsInitialized();
// If the class may move around, then don't embed it as a direct pointer.
*use_direct_type_ptr = true;
@@ -1604,7 +1604,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
}
}
}
- if (runtime->UseJit()) {
+ if (runtime->UseJitCompilation()) {
// If we are the JIT, then don't allow a direct call to the interpreter bridge since this will
// never be updated even after we compile the method.
if (cl->IsQuickToInterpreterBridge(
@@ -1636,7 +1636,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
bool must_use_direct_pointers = false;
mirror::DexCache* dex_cache = declaring_class->GetDexCache();
if (target_method->dex_file == dex_cache->GetDexFile() &&
- !(runtime->UseJit() && dex_cache->GetResolvedMethod(
+ !(runtime->UseJitCompilation() && dex_cache->GetResolvedMethod(
method->GetDexMethodIndex(), pointer_size) == nullptr)) {
target_method->dex_method_index = method->GetDexMethodIndex();
} else {
@@ -1673,7 +1673,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
break;
}
}
- if (method_in_image || compiling_boot || runtime->UseJit()) {
+ if (method_in_image || compiling_boot || runtime->UseJitCompilation()) {
// We know we must be able to get to the method in the image, so use that pointer.
// In the case where we are the JIT, we can always use direct pointers since we know where
// the method and its code are / will be. We don't sharpen to interpreter bridge since we
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 45d23fe516..197e473473 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5183,10 +5183,10 @@ HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind(
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
// TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index e8e6b68975..9680f2bf45 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4010,10 +4010,10 @@ HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 748fa267b9..6dc480bbee 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5965,7 +5965,7 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
DCHECK(GetCompilerOptions().GetCompilePic());
FALLTHROUGH_INTENDED;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit()); // Note: boot image is also non-JIT.
+ DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT.
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
// TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
@@ -5977,7 +5977,7 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9a4c67b690..96ec09c2a8 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5402,10 +5402,10 @@ HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind(
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 247f82574a..77b72a29f6 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -308,7 +308,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
// Check if we can use an inline cache.
ArtMethod* caller = graph_->GetArtMethod();
- if (Runtime::Current()->UseJit()) {
+ if (Runtime::Current()->UseJitCompilation()) {
// Under JIT, we should always know the caller.
DCHECK(caller != nullptr);
ScopedProfilingInfoInlineUse spiis(caller, soa.Self());
@@ -634,7 +634,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic) {
// This optimization only works under JIT for now.
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
if (graph_->GetInstructionSet() == kMips64) {
// TODO: Support HClassTableGet for mips64.
return false;
@@ -828,7 +828,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
if (!method->GetDeclaringClass()->IsVerified()) {
uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
- if (Runtime::Current()->UseJit() ||
+ if (Runtime::Current()->UseJitCompilation() ||
!compiler_driver_->IsMethodVerifiedWithoutFailures(
method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 7a1bb316e4..08bd35f14a 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -99,7 +99,7 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (direct_method != 0u) { // Should we use a direct pointer to the method?
// Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
// kDirectAddress would be fine for image methods, we don't support it at the moment.
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now?
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
method_load_data = direct_method;
@@ -109,7 +109,7 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
} else { // Use dex cache.
DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile());
if (use_pc_relative_instructions) { // Can we use PC-relative access to the dex cache arrays?
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
&graph_->GetDexFile());
@@ -121,7 +121,7 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (direct_code != 0u) { // Should we use a direct pointer to the code?
// Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
// while kCallDirect would be fine for image methods, we don't support it at the moment.
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now?
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
direct_code_ptr = direct_code;
@@ -174,7 +174,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) {
if (compiler_driver_->IsBootImage()) {
// Compiling boot image. Resolve the string and allocate it if needed.
- DCHECK(!runtime->UseJit());
+ DCHECK(!runtime->UseJitCompilation());
mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
CHECK(string != nullptr);
if (!compiler_driver_->GetSupportBootImageFixup()) {
@@ -187,7 +187,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) {
? HLoadString::LoadKind::kBootImageLinkTimePcRelative
: HLoadString::LoadKind::kBootImageLinkTimeAddress;
}
- } else if (runtime->UseJit()) {
+ } else if (runtime->UseJitCompilation()) {
// TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
// DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
mirror::String* string = dex_cache->GetResolvedString(string_index);
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 34d19d151b..06156f5cf8 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -276,7 +276,7 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
- CHECK(!runtime->UseJit());
+ CHECK(!runtime->UseJitCompilation());
const void* oat_quick_code = runtime->GetClassLinker()->GetOatMethodQuickCodeFor(this);
CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
<< "Don't call compiled code when -Xint " << PrettyMethod(this);
@@ -481,7 +481,7 @@ void ArtMethod::CopyFrom(ArtMethod* src, size_t image_pointer_size) {
// to the JIT code, but this would require taking the JIT code cache lock to notify
// it, which we do not want at this level.
Runtime* runtime = Runtime::Current();
- if (runtime->GetJit() != nullptr) {
+ if (runtime->UseJitCompilation()) {
if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 774c543acf..72569e188f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2032,6 +2032,7 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) {
Runtime* const runtime = Runtime::Current();
JavaVMExt* const vm = runtime->GetJavaVM();
vm->DeleteWeakGlobalRef(self, data.weak_root);
+ // Notify the JIT that we need to remove the methods and/or profiling info.
if (runtime->GetJit() != nullptr) {
jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
if (code_cache != nullptr) {
@@ -2749,7 +2750,7 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void*
}
if (runtime->IsNativeDebuggable()) {
- DCHECK(runtime->UseJit() && runtime->GetJit()->JitAtFirstUse());
+ DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
// If we are doing native debugging, ignore application's AOT code,
// since we want to JIT it with extra stackmaps for native debugging.
// On the other hand, keep all AOT code from the boot image, since the
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index c2f772f876..df5aa0a75c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2687,8 +2687,8 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
}
- if ((gc_type == collector::kGcTypeFull) && runtime->UseJit()) {
- // It's time to clear all inline caches, in case some classes can be unloaded.
+ // It's time to clear all inline caches, in case some classes can be unloaded.
+ if ((gc_type == collector::kGcTypeFull) && (runtime->GetJit() != nullptr)) {
runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self);
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 654cea088f..1a5621e4a0 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -51,7 +51,7 @@ bool Jit::generate_debug_info_ = false;
JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) {
auto* jit_options = new JitOptions;
- jit_options->use_jit_ = options.GetOrDefault(RuntimeArgumentMap::UseJIT);
+ jit_options->use_jit_compilation_ = options.GetOrDefault(RuntimeArgumentMap::UseJitCompilation);
jit_options->code_cache_initial_capacity_ =
options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheInitialCapacity);
@@ -130,9 +130,11 @@ Jit::Jit() : dump_info_on_shutdown_(false),
cumulative_timings_("JIT timings"),
memory_use_("Memory used for compilation", 16),
lock_("JIT memory use lock"),
+ use_jit_compilation_(true),
save_profiling_info_(false) {}
Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
+ DCHECK(options->UseJitCompilation() || options->GetSaveProfilingInfo());
std::unique_ptr<Jit> jit(new Jit);
jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
@@ -146,6 +148,7 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
if (jit->GetCodeCache() == nullptr) {
return nullptr;
}
+ jit->use_jit_compilation_ = options->UseJitCompilation();
jit->save_profiling_info_ = options->GetSaveProfilingInfo();
VLOG(jit) << "JIT created with initial_capacity="
<< PrettySize(options->GetCodeCacheInitialCapacity())
@@ -225,6 +228,7 @@ bool Jit::LoadCompiler(std::string* error_msg) {
}
bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) {
+ DCHECK(Runtime::Current()->UseJitCompilation());
DCHECK(!method->IsRuntimeMethod());
// Don't compile the method if it has breakpoints.
@@ -329,8 +333,12 @@ Jit::~Jit() {
}
void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) {
+ if (!Runtime::Current()->UseJitCompilation()) {
+ // No need to notify if we only use the JIT to save profiles.
+ return;
+ }
jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->generate_debug_info_) {
+ if (jit->generate_debug_info_) {
DCHECK(jit->jit_types_loaded_ != nullptr);
jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1);
}
@@ -604,22 +612,24 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_
}
// Avoid jumping more than one state at a time.
new_count = std::min(new_count, hot_method_threshold_ - 1);
- } else if (starting_count < hot_method_threshold_) {
- if ((new_count >= hot_method_threshold_) &&
- !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
- DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
- }
- // Avoid jumping more than one state at a time.
- new_count = std::min(new_count, osr_method_threshold_ - 1);
- } else if (starting_count < osr_method_threshold_) {
- if (!with_backedges) {
- // If the samples don't contain any back edge, we don't increment the hotness.
- return;
- }
- if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) {
- DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ } else if (use_jit_compilation_) {
+ if (starting_count < hot_method_threshold_) {
+ if ((new_count >= hot_method_threshold_) &&
+ !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+ }
+ // Avoid jumping more than one state at a time.
+ new_count = std::min(new_count, osr_method_threshold_ - 1);
+ } else if (starting_count < osr_method_threshold_) {
+ if (!with_backedges) {
+ // If the samples don't contain any back edge, we don't increment the hotness.
+ return;
+ }
+ if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ }
}
}
// Update hotness counter
@@ -627,7 +637,8 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_
}
void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
- if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) {
// The compiler requires a ProfilingInfo object.
ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
JitCompileTask compile_task(method, JitCompileTask::kCompile);
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 8198c18ebb..954a1f7f8f 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -87,6 +87,15 @@ class Jit {
return priority_thread_weight_;
}
+ // Returns false if we only need to save profile information and not compile methods.
+ bool UseJitCompilation() const {
+ return use_jit_compilation_;
+ }
+
+ bool SaveProfilingInfo() const {
+ return save_profiling_info_;
+ }
+
// Wait until there is no more pending compilation tasks.
void WaitForCompilationToFinish(Thread* self);
@@ -179,6 +188,7 @@ class Jit {
std::unique_ptr<jit::JitCodeCache> code_cache_;
+ bool use_jit_compilation_;
bool save_profiling_info_;
static bool generate_debug_info_;
uint16_t hot_method_threshold_;
@@ -218,22 +228,22 @@ class JitOptions {
bool GetSaveProfilingInfo() const {
return save_profiling_info_;
}
- bool UseJIT() const {
- return use_jit_;
+ bool UseJitCompilation() const {
+ return use_jit_compilation_;
}
- void SetUseJIT(bool b) {
- use_jit_ = b;
+ void SetUseJitCompilation(bool b) {
+ use_jit_compilation_ = b;
}
void SetSaveProfilingInfo(bool b) {
save_profiling_info_ = b;
}
void SetJitAtFirstUse() {
- use_jit_ = true;
+ use_jit_compilation_ = true;
compile_threshold_ = 0;
}
private:
- bool use_jit_;
+ bool use_jit_compilation_;
size_t code_cache_initial_capacity_;
size_t code_cache_max_capacity_;
size_t compile_threshold_;
@@ -244,7 +254,7 @@ class JitOptions {
bool save_profiling_info_;
JitOptions()
- : use_jit_(false),
+ : use_jit_compilation_(false),
code_cache_initial_capacity_(0),
code_cache_max_capacity_(0),
compile_threshold_(0),
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 7a9d2506dc..99b8dfcf3b 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -285,7 +285,7 @@ void ProfileSaver::Start(const std::string& output_filename,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
const std::string& app_data_dir) {
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->SaveProfileInfo());
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
@@ -520,4 +520,32 @@ void ProfileSaver::DumpInfo(std::ostream& os) {
<< max_number_of_profile_entries_cached_ << '\n';
}
+
+void ProfileSaver::ForceProcessProfiles() {
+ ProfileSaver* saver = nullptr;
+ {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ saver = instance_;
+ }
+ // TODO(calin): this is not actually thread safe as the instance_ may have been deleted,
+ // but we only use this in testing when we now this won't happen.
+ // Refactor the way we handle the instance so that we don't end up in this situation.
+ if (saver != nullptr) {
+ saver->ProcessProfilingInfo();
+ }
+}
+
+bool ProfileSaver::HasSeenMethod(const std::string& profile,
+ const DexFile* dex_file,
+ uint16_t method_idx) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ ProfileCompilationInfo* info = instance_->GetCachedProfiledInfo(profile);
+ if (info != nullptr) {
+ return info->ContainsMethod(MethodReference(dex_file, method_idx));
+ }
+ }
+ return false;
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 0a222bfdcd..4f3cdc28cb 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -49,6 +49,12 @@ class ProfileSaver {
// If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing.
static void DumpInstanceInfo(std::ostream& os);
+ // Just for testing purpose.
+ static void ForceProcessProfiles();
+ static bool HasSeenMethod(const std::string& profile,
+ const DexFile* dex_file,
+ uint16_t method_idx);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -65,7 +71,10 @@ class ProfileSaver {
void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_);
// Processes the existing profiling info from the jit code cache and returns
// true if it needed to be saved to disk.
- bool ProcessProfilingInfo();
+ bool ProcessProfilingInfo()
+ REQUIRES(!Locks::profiler_lock_)
+ REQUIRES(!Locks::mutator_lock_);
+
// Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c8d429158c..755159f88c 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -153,7 +153,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define("-Xusejit:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
- .IntoKey(M::UseJIT)
+ .IntoKey(M::UseJitCompilation)
.Define("-Xjitinitialsize:_")
.WithType<MemoryKiB>()
.IntoKey(M::JITCodeCacheInitialCapacity)
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index c7ccee2125..1dea562b5e 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -434,7 +434,7 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
InlineMethod* result) {
DCHECK(verifier != nullptr);
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
}
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index a785ecba3b..237fdaab0e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -509,7 +509,7 @@ void QuickExceptionHandler::DeoptimizeSingleFrame() {
// Compiled code made an explicit deoptimization.
ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod();
DCHECK(deopt_method != nullptr);
- if (Runtime::Current()->UseJit()) {
+ if (Runtime::Current()->UseJitCompilation()) {
Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor(
deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader());
} else {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f0510affc5..6f2ad5f18c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -558,15 +558,21 @@ bool Runtime::Start() {
started_ = true;
- if (jit_options_->UseJIT()) {
+ // Create the JIT either if we have to use JIT compilation or save profiling info.
+ // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
+ // recoding profiles. Maybe we should consider changing the name to be more clear it's
+ // not only about compiling. b/28295073.
+ if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
std::string error_msg;
if (!IsZygote()) {
// If we are the zygote then we need to wait until after forking to create the code cache
// due to SELinux restrictions on r/w/x memory regions.
CreateJit();
- } else if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
- // Try to load compiler pre zygote to reduce PSS. b/27744947
- LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+ } else if (jit_options_->UseJitCompilation()) {
+ if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
+ // Try to load compiler pre zygote to reduce PSS. b/27744947
+ LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+ }
}
}
@@ -713,7 +719,11 @@ void Runtime::InitNonZygoteOrPostFork(
// before fork aren't attributed to an app.
heap_->ResetGcPerformanceInfo();
- if (!is_system_server && !safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) {
+
+ if (!is_system_server &&
+ !safe_mode_ &&
+ (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
+ jit_.get() == nullptr) {
// Note that when running ART standalone (not zygote, nor zygote fork),
// the jit may have already been created.
CreateJit();
@@ -1016,7 +1026,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
// this case.
// If runtime_options doesn't have UseJIT set to true then CreateFromRuntimeArguments returns
// null and we don't create the jit.
- jit_options_->SetUseJIT(false);
+ jit_options_->SetUseJitCompilation(false);
+ jit_options_->SetSaveProfilingInfo(false);
}
// Allocate a global table of boxed lambda objects <-> closures.
@@ -1996,4 +2007,14 @@ void Runtime::RegisterSensitiveThread() const {
Thread::SetJitSensitiveThread();
}
+// Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
+bool Runtime::UseJitCompilation() const {
+ return (jit_ != nullptr) && jit_->UseJitCompilation();
+}
+
+// Returns true if profile saving is enabled. GetJit() will be not null in this case.
+bool Runtime::SaveProfileInfo() const {
+ return (jit_ != nullptr) && jit_->SaveProfilingInfo();
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index fd4b5c8956..1394462fd1 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -127,7 +127,7 @@ class Runtime {
// IsAotCompiler for compilers that don't have a running runtime. Only dex2oat currently.
bool IsAotCompiler() const {
- return !UseJit() && IsCompiler();
+ return !UseJitCompilation() && IsCompiler();
}
// IsCompiler is any runtime which has a running compiler, either dex2oat or JIT.
@@ -452,9 +452,11 @@ class Runtime {
jit::Jit* GetJit() {
return jit_.get();
}
- bool UseJit() const {
- return jit_.get() != nullptr;
- }
+
+ // Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
+ bool UseJitCompilation() const;
+ // Returns true if profile saving is enabled. GetJit() will be not null in this case.
+ bool SaveProfileInfo() const;
void PreZygoteFork();
bool InitZygote();
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6433c3352f..4e47953f3f 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -66,7 +66,7 @@ RUNTIME_OPTIONS_KEY (Unit, IgnoreMaxFootprint)
RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier))
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
-RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
+RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false)
RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold)
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 45aeb92793..e56d35a91e 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -637,8 +637,8 @@ static void AssertPcIsWithinQuickCode(ArtMethod* method, uintptr_t pc)
// If we are the JIT then we may have just compiled the method after the
// IsQuickToInterpreterBridge check.
- jit::Jit* const jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->UseJitCompilation() && runtime->GetJit()->GetCodeCache()->ContainsPc(code)) {
return;
}
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index 2a5b2c954d..bd8f0a9520 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -75,7 +75,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInOsrCode(JNIEnv* env,
extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInInterpreter(JNIEnv* env,
jclass,
jstring method_name) {
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
// The return value is irrelevant if we're not using JIT.
return false;
}
@@ -111,7 +111,7 @@ class ProfilingInfoVisitor : public StackVisitor {
extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv* env,
jclass,
jstring method_name) {
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
return;
}
ScopedUtfChars chars(env, method_name);
@@ -151,7 +151,7 @@ class OsrCheckVisitor : public StackVisitor {
extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv* env,
jclass,
jstring method_name) {
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
return;
}
ScopedUtfChars chars(env, method_name);
diff --git a/test/595-profile-saving/expected.txt b/test/595-profile-saving/expected.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/595-profile-saving/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/595-profile-saving/info.txt b/test/595-profile-saving/info.txt
new file mode 100644
index 0000000000..5d318f5b15
--- /dev/null
+++ b/test/595-profile-saving/info.txt
@@ -0,0 +1 @@
+Check that profile recording works even when JIT compilation is not enabled.
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
new file mode 100644
index 0000000000..a7e6f8ba8c
--- /dev/null
+++ b/test/595-profile-saving/profile-saving.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "dex_file.h"
+
+#include "jit/offline_profiling_info.h"
+#include "jit/profile_saver.h"
+#include "jni.h"
+#include "method_reference.h"
+#include "mirror/class-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+#include "thread.h"
+
+namespace art {
+namespace {
+
+class CreateProfilingInfoVisitor : public StackVisitor {
+ public:
+ explicit CreateProfilingInfoVisitor(Thread* thread, const char* method_name)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ method_name_(method_name) {}
+
+ bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if (m_name.compare(method_name_) == 0) {
+ ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
+ method_index_ = m->GetDexMethodIndex();
+ return false;
+ }
+ return true;
+ }
+
+ int method_index_ = -1;
+ const char* const method_name_;
+};
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
+ jclass,
+ jstring method_name) {
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ CreateProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
+ visitor.WalkStack();
+ return visitor.method_index_;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
+ ProfileSaver::ForceProcessProfiles();
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(
+ JNIEnv* env, jclass cls, jstring filename, jint method_index) {
+ ScopedUtfChars filename_chars(env, filename);
+ CHECK(filename_chars.c_str() != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile* dex_file = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile();
+ return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
+ dex_file,
+ static_cast<uint16_t>(method_index));
+}
+
+} // namespace
+} // namespace art
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
new file mode 100644
index 0000000000..f12fac9207
--- /dev/null
+++ b/test/595-profile-saving/run
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# Use
+# --compiler-filter=interpret-only to make sure that the test is not compiled AOT
+# -XOatFileManagerCompilerFilter:interpret-only to make sure the test is not compiled
+# when loaded (by PathClassLoader)
+# -Xjitsaveprofilinginfo to enable profile saving
+# -Xusejit:false to disable jit and only test profiles.
+exec ${RUN} \
+ -Xcompiler-option --compiler-filter=interpret-only \
+ --runtime-option -XOatFileManagerCompilerFilter:interpret-only \
+ --runtime-option -Xjitsaveprofilinginfo \
+ --runtime-option -Xusejit:false \
+ "${@}"
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
new file mode 100644
index 0000000000..039503f7a4
--- /dev/null
+++ b/test/595-profile-saving/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ File file = null;
+ try {
+ file = createTempFile();
+ // String codePath = getDexBaseLocation();
+ String codePath = System.getenv("DEX_LOCATION") + "/595-profile-saving.jar";
+ VMRuntime.registerAppInfo(file.getPath(),
+ System.getenv("DEX_LOCATION"),
+ new String[] {codePath},
+ /* foreignProfileDir */ null);
+
+ int methodIdx = $opt$noinline$testProfile();
+ ensureProfileProcessing();
+ if (!presentInProfile(file.getPath(), methodIdx)) {
+ throw new RuntimeException("Method with index " + methodIdx + " not in the profile");
+ }
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+ }
+
+ public static int $opt$noinline$testProfile() {
+ if (doThrow) throw new Error();
+ // Make sure we have a profile info for this method without the need to loop.
+ return ensureProfilingInfo("$opt$noinline$testProfile");
+ }
+
+ // Return the dex method index.
+ public static native int ensureProfilingInfo(String methodName);
+ // Ensures the profile saver does its usual processing.
+ public static native void ensureProfileProcessing();
+ // Checks if the profiles saver knows about the method.
+ public static native boolean presentInProfile(String profile, int methodIdx);
+
+ public static boolean doThrow = false;
+ private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+ static native String getProfileInfoDump(
+ String filename);
+
+ 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 {
+ 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);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String profile, String appDir,
+ String[] codePaths, String foreignDir) throws Exception {
+ registerAppInfoMethod.invoke(null, profile, appDir, codePaths, foreignDir);
+ }
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index e547c72c0e..464da2e1de 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -41,7 +41,8 @@ LIBARTTEST_COMMON_SRC_FILES := \
497-inlining-and-class-loader/clear_dex_cache.cc \
543-env-long-ref/env_long_ref.cc \
566-polymorphic-inlining/polymorphic_inline.cc \
- 570-checker-osr/osr.cc
+ 570-checker-osr/osr.cc \
+ 595-profile-saving/profile-saving.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so