1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18 #include <android-base/macros.h>
19 #include <sys/resource.h>
20
21 #include "art_field.h"
22 #include "art_method-inl.h"
23 #include "base/pointer_size.h"
24 #include "common_throws.h"
25 #include "dex/dex_file-inl.h"
26 #include "dex/dex_file_types.h"
27 #include "gc/heap.h"
28 #include "instrumentation.h"
29 #include "jit/jit.h"
30 #include "jit/jit_code_cache.h"
31 #include "jit/profile_saver.h"
32 #include "jit/profiling_info.h"
33 #include "jni.h"
34 #include "jni/jni_internal.h"
35 #include "mirror/class-inl.h"
36 #include "mirror/class.h"
37 #include "mirror/executable.h"
38 #include "nativehelper/ScopedUtfChars.h"
39 #include "oat/oat.h"
40 #include "oat/oat_file.h"
41 #include "oat/oat_quick_method_header.h"
42 #include "profile/profile_compilation_info.h"
43 #include "runtime.h"
44 #include "scoped_thread_state_change-inl.h"
45 #include "scoped_thread_state_change.h"
46 #include "thread-current-inl.h"
47
48 namespace art {
49
50 // public static native boolean hasJit();
51
GetJitIfEnabled()52 static jit::Jit* GetJitIfEnabled() {
53 Runtime* runtime = Runtime::Current();
54 bool can_jit =
55 runtime != nullptr
56 && runtime->GetJit() != nullptr
57 && runtime->UseJitCompilation()
58 && runtime->GetInstrumentation()->GetCurrentInstrumentationLevel() !=
59 instrumentation::Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter;
60 return can_jit ? runtime->GetJit() : nullptr;
61 }
62
Java_Main_hasJit(JNIEnv *,jclass)63 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) {
64 return GetJitIfEnabled() != nullptr;
65 }
66
67 // public static native boolean hasOatFile();
68
Java_Main_hasOatFile(JNIEnv * env,jclass cls)69 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasOatFile(JNIEnv* env, jclass cls) {
70 ScopedObjectAccess soa(env);
71
72 ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
73 const DexFile& dex_file = klass->GetDexFile();
74 const OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
75 return (oat_dex_file != nullptr) ? JNI_TRUE : JNI_FALSE;
76 }
77
Java_Main_getCompilerFilter(JNIEnv * env,jclass caller,jclass cls)78 extern "C" JNIEXPORT jobject JNICALL Java_Main_getCompilerFilter(JNIEnv* env,
79 [[maybe_unused]] jclass caller,
80 jclass cls) {
81 ScopedObjectAccess soa(env);
82
83 ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
84 const DexFile& dex_file = klass->GetDexFile();
85 const OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
86 if (oat_dex_file == nullptr) {
87 return nullptr;
88 }
89
90 std::string filter =
91 CompilerFilter::NameOfFilter(oat_dex_file->GetOatFile()->GetCompilerFilter());
92 return soa.AddLocalReference<jobject>(
93 mirror::String::AllocFromModifiedUtf8(soa.Self(), filter.c_str()));
94 }
95
96 // public static native boolean runtimeIsSoftFail();
97
Java_Main_runtimeIsSoftFail(JNIEnv * env,jclass cls)98 extern "C" JNIEXPORT jboolean JNICALL Java_Main_runtimeIsSoftFail([[maybe_unused]] JNIEnv* env,
99 [[maybe_unused]] jclass cls) {
100 return Runtime::Current()->IsVerificationSoftFail() ? JNI_TRUE : JNI_FALSE;
101 }
102
103 // public static native boolean hasImage();
104
Java_Main_hasImage(JNIEnv * env,jclass cls)105 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasImage([[maybe_unused]] JNIEnv* env,
106 [[maybe_unused]] jclass cls) {
107 return Runtime::Current()->GetHeap()->HasBootImageSpace();
108 }
109
110 // public static native boolean isImageDex2OatEnabled();
111
Java_Main_isImageDex2OatEnabled(JNIEnv * env,jclass cls)112 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isImageDex2OatEnabled([[maybe_unused]] JNIEnv* env,
113 [[maybe_unused]] jclass cls) {
114 return Runtime::Current()->IsImageDex2OatEnabled();
115 }
116
117 // public static native boolean compiledWithOptimizing();
118 // Did we use the optimizing compiler to compile this?
119
Java_Main_compiledWithOptimizing(JNIEnv * env,jclass cls)120 extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* env, jclass cls) {
121 ScopedObjectAccess soa(env);
122
123 ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
124 const DexFile& dex_file = klass->GetDexFile();
125 const OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
126 if (oat_dex_file == nullptr) {
127 // Could be JIT, which also uses optimizing, but conservatively say no.
128 return JNI_FALSE;
129 }
130 const OatFile* oat_file = oat_dex_file->GetOatFile();
131 CHECK(oat_file != nullptr);
132
133 const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
134 if (cmd_line == nullptr) {
135 // Vdex-only execution, conservatively say no.
136 return JNI_FALSE;
137 }
138
139 // Check the backend.
140 constexpr const char* kCompilerBackend = "--compiler-backend=";
141 const char* backend = strstr(cmd_line, kCompilerBackend);
142 if (backend != nullptr) {
143 // If it's set, make sure it's optimizing.
144 backend += strlen(kCompilerBackend);
145 if (strncmp(backend, "Optimizing", strlen("Optimizing")) != 0) {
146 return JNI_FALSE;
147 }
148 }
149
150 // Check the filter.
151 constexpr const char* kCompilerFilter = "--compiler-filter=";
152 const char* filter = strstr(cmd_line, kCompilerFilter);
153 if (filter != nullptr) {
154 filter += strlen(kCompilerFilter);
155 const char* end = strchr(filter, ' ');
156 std::string string_filter(filter, (end == nullptr) ? strlen(filter) : end - filter);
157 CompilerFilter::Filter compiler_filter;
158 bool success = CompilerFilter::ParseCompilerFilter(string_filter.c_str(), &compiler_filter);
159 CHECK(success);
160 return CompilerFilter::IsAotCompilationEnabled(compiler_filter) ? JNI_TRUE : JNI_FALSE;
161 }
162
163 // No filter passed, assume default has AOT.
164 return JNI_TRUE;
165 }
166
GetMethod(ScopedObjectAccess & soa,jclass cls,const ScopedUtfChars & chars)167 static ArtMethod* GetMethod(ScopedObjectAccess& soa, jclass cls, const ScopedUtfChars& chars)
168 REQUIRES_SHARED(Locks::mutator_lock_) {
169 CHECK(chars.c_str() != nullptr);
170 ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
171 chars.c_str(), kRuntimePointerSize);
172 if (method == nullptr) {
173 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(chars.c_str(),
174 kRuntimePointerSize);
175 }
176 DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
177 return method;
178 }
179
Java_Main_isAotCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)180 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env,
181 jclass,
182 jclass cls,
183 jstring method_name) {
184 Thread* self = Thread::Current();
185 ScopedObjectAccess soa(self);
186 ScopedUtfChars chars(env, method_name);
187 CHECK(chars.c_str() != nullptr);
188 ArtMethod* method = GetMethod(soa, cls, chars);
189 const void* oat_code = method->GetOatMethodQuickCode(kRuntimePointerSize);
190 if (oat_code == nullptr) {
191 return false;
192 }
193 const void* actual_code = Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method);
194 return actual_code == oat_code;
195 }
196
Java_Main_hasJitCompiledEntrypoint(JNIEnv * env,jclass,jclass cls,jstring method_name)197 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env,
198 jclass,
199 jclass cls,
200 jstring method_name) {
201 jit::Jit* jit = GetJitIfEnabled();
202 if (jit == nullptr) {
203 return false;
204 }
205 Thread* self = Thread::Current();
206 ScopedObjectAccess soa(self);
207 ScopedUtfChars chars(env, method_name);
208 ArtMethod* method = GetMethod(soa, cls, chars);
209 ScopedAssertNoThreadSuspension sants(__FUNCTION__);
210 return jit->GetCodeCache()->ContainsPc(
211 Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method));
212 }
213
Java_Main_hasJitCompiledCode(JNIEnv * env,jclass,jclass cls,jstring method_name)214 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env,
215 jclass,
216 jclass cls,
217 jstring method_name) {
218 jit::Jit* jit = GetJitIfEnabled();
219 if (jit == nullptr) {
220 return false;
221 }
222 Thread* self = Thread::Current();
223 ScopedObjectAccess soa(self);
224 ScopedUtfChars chars(env, method_name);
225 ArtMethod* method = GetMethod(soa, cls, chars);
226 return jit->GetCodeCache()->ContainsMethod(method);
227 }
228
ForceJitCompiled(Thread * self,ArtMethod * method,CompilationKind kind)229 static void ForceJitCompiled(Thread* self,
230 ArtMethod* method,
231 CompilationKind kind) REQUIRES(!Locks::mutator_lock_) {
232 // TODO(mythria): Update this check once we support method entry / exit hooks directly from
233 // JIT code instead of installing EntryExit stubs.
234 if (Runtime::Current()->GetInstrumentation()->EntryExitStubsInstalled() &&
235 (method->IsNative() || !Runtime::Current()->IsJavaDebuggable())) {
236 return;
237 }
238
239 {
240 ScopedObjectAccess soa(self);
241 if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
242 std::string msg(method->PrettyMethod());
243 msg += ": is not safe to jit!";
244 ThrowIllegalStateException(msg.c_str());
245 return;
246 }
247 // We force visible initialization of the declaring class to make sure the method
248 // doesn't keep the resolution stub as entrypoint.
249 StackHandleScope<1> hs(self);
250 Handle<mirror::Class> h_klass(hs.NewHandle(method->GetDeclaringClass()));
251 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
252 if (!class_linker->EnsureInitialized(self, h_klass, true, true)) {
253 self->AssertPendingException();
254 return;
255 }
256 if (UNLIKELY(!h_klass->IsInitialized())) {
257 // Must be initializing in this thread.
258 CHECK_EQ(h_klass->GetStatus(), ClassStatus::kInitializing);
259 CHECK_EQ(h_klass->GetClinitThreadId(), self->GetTid());
260 std::string msg(method->PrettyMethod());
261 msg += ": is not safe to jit because the class is being initialized in this thread!";
262 ThrowIllegalStateException(msg.c_str());
263 return;
264 }
265 if (!h_klass->IsVisiblyInitialized()) {
266 ScopedThreadSuspension sts(self, ThreadState::kNative);
267 class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
268 }
269 }
270 jit::Jit* jit = GetJitIfEnabled();
271 jit::JitCodeCache* code_cache = jit->GetCodeCache();
272 // Update the code cache to make sure the JIT code does not get deleted.
273 // Note: this will apply to all JIT compilations.
274 code_cache->SetGarbageCollectCode(false);
275 if (jit->JitAtFirstUse()) {
276 ScopedObjectAccess soa(self);
277 jit->CompileMethod(method, self, kind, /*prejit=*/ false);
278 return;
279 }
280 if (kind == CompilationKind::kBaseline || jit->GetJitCompiler()->IsBaselineCompiler()) {
281 ScopedObjectAccess soa(self);
282 if (jit->TryPatternMatch(method, CompilationKind::kBaseline)) {
283 return;
284 }
285 jit->MaybeEnqueueCompilation(method, self);
286 } else {
287 jit->EnqueueOptimizedCompilation(method, self);
288 }
289 do {
290 // Sleep to yield to the compiler thread.
291 usleep(1000);
292 const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
293 if (code_cache->ContainsPc(entry_point)) {
294 // If we're running baseline or not requesting optimized, we're good to go.
295 if (jit->GetJitCompiler()->IsBaselineCompiler() || kind != CompilationKind::kOptimized) {
296 break;
297 }
298 // If we're requesting optimized, check that we did get the method
299 // compiled optimized.
300 OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(entry_point);
301 if (!CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
302 break;
303 }
304 }
305 } while (true);
306 }
307
Java_Main_ensureMethodJitCompiled(JNIEnv *,jclass,jobject meth)308 extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
309 jit::Jit* jit = GetJitIfEnabled();
310 if (jit == nullptr) {
311 return;
312 }
313
314 Thread* self = Thread::Current();
315 ArtMethod* method;
316 {
317 ScopedObjectAccess soa(self);
318 method = ArtMethod::FromReflectedMethod(soa, meth);
319 }
320 ForceJitCompiled(self, method, CompilationKind::kOptimized);
321 }
322
Java_Main_ensureJitCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)323 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
324 jclass,
325 jclass cls,
326 jstring method_name) {
327 jit::Jit* jit = GetJitIfEnabled();
328 if (jit == nullptr) {
329 return;
330 }
331
332 Thread* self = Thread::Current();
333 ArtMethod* method = nullptr;
334 {
335 ScopedObjectAccess soa(self);
336
337 ScopedUtfChars chars(env, method_name);
338 method = GetMethod(soa, cls, chars);
339 }
340 ForceJitCompiled(self, method, CompilationKind::kOptimized);
341 }
342
Java_Main_ensureJitBaselineCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)343 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitBaselineCompiled(JNIEnv* env,
344 jclass,
345 jclass cls,
346 jstring method_name) {
347 jit::Jit* jit = GetJitIfEnabled();
348 if (jit == nullptr) {
349 return;
350 }
351
352 Thread* self = Thread::Current();
353 ArtMethod* method = nullptr;
354 {
355 ScopedObjectAccess soa(self);
356
357 ScopedUtfChars chars(env, method_name);
358 method = GetMethod(soa, cls, chars);
359 }
360 ForceJitCompiled(self, method, CompilationKind::kBaseline);
361 }
362
Java_Main_hasSingleImplementation(JNIEnv * env,jclass,jclass cls,jstring method_name)363 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasSingleImplementation(JNIEnv* env,
364 jclass,
365 jclass cls,
366 jstring method_name) {
367 ArtMethod* method = nullptr;
368 ScopedObjectAccess soa(Thread::Current());
369 ScopedUtfChars chars(env, method_name);
370 CHECK(chars.c_str() != nullptr);
371 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
372 chars.c_str(), kRuntimePointerSize);
373 return method->HasSingleImplementation();
374 }
375
Java_Main_getHotnessCounter(JNIEnv * env,jclass,jclass cls,jstring method_name)376 extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
377 jclass,
378 jclass cls,
379 jstring method_name) {
380 ScopedObjectAccess soa(Thread::Current());
381 ScopedUtfChars chars(env, method_name);
382 CHECK(chars.c_str() != nullptr);
383 ArtMethod* method =
384 soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(chars.c_str(),
385 kRuntimePointerSize);
386 if (method != nullptr) {
387 return method->GetCounter();
388 }
389
390 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(chars.c_str(),
391 kRuntimePointerSize);
392 if (method != nullptr) {
393 return method->GetCounter();
394 }
395
396 return std::numeric_limits<int32_t>::min();
397 }
398
Java_Main_numberOfDeoptimizations(JNIEnv *,jclass)399 extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jclass) {
400 return Runtime::Current()->GetNumberOfDeoptimizations();
401 }
402
Java_Main_fetchProfiles(JNIEnv *,jclass)403 extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) {
404 jit::Jit* jit = GetJitIfEnabled();
405 if (jit == nullptr) {
406 return;
407 }
408 jit::JitCodeCache* code_cache = jit->GetCodeCache();
409 std::vector<ProfileMethodInfo> unused_vector;
410 std::set<std::string> unused_locations;
411 unused_locations.insert("fake_location");
412 ScopedObjectAccess soa(Thread::Current());
413 code_cache->GetProfiledMethods(unused_locations, unused_vector, /*inline_cache_threshold=*/0);
414 }
415
Java_Main_waitForCompilation(JNIEnv *,jclass)416 extern "C" JNIEXPORT void JNICALL Java_Main_waitForCompilation(JNIEnv*, jclass) {
417 jit::Jit* jit = Runtime::Current()->GetJit();
418 if (jit != nullptr) {
419 jit->WaitForCompilationToFinish(Thread::Current());
420 }
421 }
422
Java_Main_stopJit(JNIEnv *,jclass)423 extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
424 jit::Jit* jit = Runtime::Current()->GetJit();
425 if (jit != nullptr) {
426 jit->Stop();
427 }
428 }
429
Java_Main_startJit(JNIEnv *,jclass)430 extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
431 jit::Jit* jit = Runtime::Current()->GetJit();
432 if (jit != nullptr) {
433 jit->Start();
434 }
435 }
436
Java_Main_getJitThreshold(JNIEnv *,jclass)437 extern "C" JNIEXPORT jint JNICALL Java_Main_getJitThreshold(JNIEnv*, jclass) {
438 jit::Jit* jit = Runtime::Current()->GetJit();
439 return (jit != nullptr) ? jit->HotMethodThreshold() : 0;
440 }
441
Java_Main_deoptimizeBootImage(JNIEnv *,jclass)442 extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeBootImage(JNIEnv*, jclass) {
443 ScopedSuspendAll ssa(__FUNCTION__);
444 Runtime::Current()->DeoptimizeBootImage();
445 }
446
Java_Main_deoptimizeNativeMethod(JNIEnv * env,jclass,jclass cls,jstring method_name)447 extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeNativeMethod(JNIEnv* env,
448 jclass,
449 jclass cls,
450 jstring method_name) {
451 Thread* self = Thread::Current();
452 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
453 // Make initialized classes visibly initialized to avoid entrypoint being set to boot JNI stub
454 // after deoptimize.
455 class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
456 ScopedObjectAccess soa(self);
457 ScopedUtfChars chars(env, method_name);
458 ArtMethod* method = GetMethod(soa, cls, chars);
459 CHECK(method->IsNative());
460 Runtime::Current()->GetInstrumentation()->ReinitializeMethodsCode(method);
461 }
462
Java_Main_isDebuggable(JNIEnv *,jclass)463 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDebuggable(JNIEnv*, jclass) {
464 return Runtime::Current()->IsJavaDebuggable() ? JNI_TRUE : JNI_FALSE;
465 }
466
Java_Main_setTargetSdkVersion(JNIEnv *,jclass,jint version)467 extern "C" JNIEXPORT void JNICALL Java_Main_setTargetSdkVersion(JNIEnv*, jclass, jint version) {
468 Runtime::Current()->SetTargetSdkVersion(static_cast<uint32_t>(version));
469 }
470
Java_Main_genericFieldOffset(JNIEnv * env,jclass,jobject fld)471 extern "C" JNIEXPORT jlong JNICALL Java_Main_genericFieldOffset(JNIEnv* env, jclass, jobject fld) {
472 jfieldID fid = env->FromReflectedField(fld);
473 ScopedObjectAccess soa(env);
474 ArtField* af = jni::DecodeArtField(fid);
475 return af->GetOffset().Int32Value();
476 }
477
Java_Main_isObsoleteObject(JNIEnv * env,jclass,jclass c)478 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isObsoleteObject(JNIEnv* env, jclass, jclass c) {
479 ScopedObjectAccess soa(env);
480 return soa.Decode<mirror::Class>(c)->IsObsoleteObject();
481 }
482
Java_Main_forceInterpreterOnThread(JNIEnv * env,jclass cls)483 extern "C" JNIEXPORT void JNICALL Java_Main_forceInterpreterOnThread(JNIEnv* env,
484 [[maybe_unused]] jclass cls) {
485 ScopedObjectAccess soa(env);
486 MutexLock thread_list_mu(soa.Self(), *Locks::thread_list_lock_);
487 soa.Self()->IncrementForceInterpreterCount();
488 }
489
Java_Main_setAsyncExceptionsThrown(JNIEnv * env,jclass cls)490 extern "C" JNIEXPORT void JNICALL Java_Main_setAsyncExceptionsThrown([[maybe_unused]] JNIEnv* env,
491 [[maybe_unused]] jclass cls) {
492 Runtime::Current()->SetAsyncExceptionsThrown();
493 }
494
Java_Main_setRlimitNoFile(JNIEnv *,jclass,jint value)495 extern "C" JNIEXPORT void JNICALL Java_Main_setRlimitNoFile(JNIEnv*, jclass, jint value) {
496 rlimit limit { static_cast<rlim_t>(value), static_cast<rlim_t>(value) };
497 setrlimit(RLIMIT_NOFILE, &limit);
498 }
499
Java_Main_isInImageSpace(JNIEnv * env,jclass caller,jclass cls)500 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInImageSpace(JNIEnv* env,
501 [[maybe_unused]] jclass caller,
502 jclass cls) {
503 ScopedObjectAccess soa(env);
504
505 ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
506 gc::space::Space* space =
507 Runtime::Current()->GetHeap()->FindSpaceFromObject(klass, /*fail_ok=*/true);
508 if (space == nullptr) {
509 return JNI_FALSE;
510 }
511 return space->IsImageSpace() ? JNI_TRUE : JNI_FALSE;
512 }
513
514 // Ensures the profile saver does its usual processing.
Java_Main_ensureProfileProcessing(JNIEnv *,jclass)515 extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
516 ProfileSaver::ForceProcessProfiles();
517 }
518
Java_Main_isForBootImage(JNIEnv * env,jclass,jstring filename)519 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isForBootImage(JNIEnv* env,
520 jclass,
521 jstring filename) {
522 ScopedUtfChars filename_chars(env, filename);
523 CHECK(filename_chars.c_str() != nullptr);
524
525 ProfileCompilationInfo info(/*for_boot_image=*/true);
526 bool result = info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/false);
527 return result ? JNI_TRUE : JNI_FALSE;
528 }
529
GetMethodHotnessFromProfile(JNIEnv * env,jclass c,jstring filename,jobject method)530 static ProfileCompilationInfo::MethodHotness GetMethodHotnessFromProfile(JNIEnv* env,
531 jclass c,
532 jstring filename,
533 jobject method) {
534 bool for_boot_image = Java_Main_isForBootImage(env, c, filename) == JNI_TRUE;
535 ScopedUtfChars filename_chars(env, filename);
536 CHECK(filename_chars.c_str() != nullptr);
537 ScopedObjectAccess soa(env);
538 ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
539 ArtMethod* art_method = exec->GetArtMethod();
540 MethodReference ref(art_method->GetDexFile(), art_method->GetDexMethodIndex());
541
542 ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(), for_boot_image);
543 if (!info.Load(filename_chars.c_str(), /*clear_if_invalid=*/false)) {
544 LOG(ERROR) << "Failed to load profile from " << filename;
545 return ProfileCompilationInfo::MethodHotness();
546 }
547 return info.GetMethodHotness(ref);
548 }
549
550 // Checks if the method is present in the profile.
Java_Main_presentInProfile(JNIEnv * env,jclass c,jstring filename,jobject method)551 extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
552 jclass c,
553 jstring filename,
554 jobject method) {
555 // TODO: Why do we check `hotness.IsHot()` instead of `hotness.IsInProfile()`
556 // in a method named `presentInProfile()`?
557 return GetMethodHotnessFromProfile(env, c, filename, method).IsHot() ? JNI_TRUE : JNI_FALSE;
558 }
559
560 // Checks if the method has an inline cache in the profile that contains at least the given target
561 // types.
Java_Main_hasInlineCacheInProfile(JNIEnv * env,jclass c,jstring filename,jobject method,jobjectArray target_types)562 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasInlineCacheInProfile(
563 JNIEnv* env, jclass c, jstring filename, jobject method, jobjectArray target_types) {
564 ProfileCompilationInfo::MethodHotness hotness =
565 GetMethodHotnessFromProfile(env, c, filename, method);
566 if (hotness.GetInlineCacheMap() == nullptr) {
567 return JNI_FALSE;
568 }
569 ScopedObjectAccess soa(env);
570 ObjPtr<mirror::ObjectArray<mirror::Class>> types =
571 soa.Decode<mirror::ObjectArray<mirror::Class>>(target_types);
572 for (const auto& [dex_pc, dex_pc_data] : *hotness.GetInlineCacheMap()) {
573 bool match = true;
574 for (ObjPtr<mirror::Class> type : *types.Ptr()) {
575 dex::TypeIndex expected_index = type->GetDexTypeIndex();
576 if (!expected_index.IsValid()) {
577 return JNI_FALSE;
578 }
579 if (dex_pc_data.classes.find(expected_index) == dex_pc_data.classes.end()) {
580 match = false;
581 break;
582 }
583 }
584 if (match) {
585 return JNI_TRUE;
586 }
587 }
588 return JNI_FALSE;
589 }
590
Java_Main_getCurrentGcNum(JNIEnv * env,jclass)591 extern "C" JNIEXPORT jint JNICALL Java_Main_getCurrentGcNum(JNIEnv* env, jclass) {
592 // Prevent any new GC before getting the current GC num.
593 ScopedObjectAccess soa(env);
594 gc::Heap* heap = Runtime::Current()->GetHeap();
595 heap->WaitForGcToComplete(gc::kGcCauseJitCodeCache, Thread::Current());
596 return heap->GetCurrentGcNum();
597 }
598
Java_Main_removeJitCompiledMethod(JNIEnv * env,jclass,jobject java_method,jboolean release_memory)599 extern "C" JNIEXPORT jboolean Java_Main_removeJitCompiledMethod(JNIEnv* env,
600 jclass,
601 jobject java_method,
602 jboolean release_memory) {
603 if (!Runtime::Current()->UseJitCompilation()) {
604 return JNI_FALSE;
605 }
606
607 jit::Jit* jit = Runtime::Current()->GetJit();
608 jit->WaitForCompilationToFinish(Thread::Current());
609
610 ScopedObjectAccess soa(env);
611 ArtMethod* method = ArtMethod::FromReflectedMethod(soa, java_method);
612
613 jit::JitCodeCache* code_cache = jit->GetCodeCache();
614
615 // Drop the shared mutator lock.
616 ScopedThreadSuspension self_suspension(Thread::Current(), art::ThreadState::kNative);
617 // Get exclusive mutator lock with suspend all.
618 ScopedSuspendAll suspend("Removing JIT compiled method", /*long_suspend=*/true);
619 bool removed = code_cache->RemoveMethod(method, static_cast<bool>(release_memory));
620 return removed ? JNI_TRUE : JNI_FALSE;
621 }
622
623 } // namespace art
624