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
Java_Main_isAotCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)167 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env,
168 jclass,
169 jclass cls,
170 jstring method_name) {
171 Thread* self = Thread::Current();
172 ScopedObjectAccess soa(self);
173 ScopedUtfChars chars(env, method_name);
174 CHECK(chars.c_str() != nullptr);
175 ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
176 chars.c_str(), kRuntimePointerSize);
177 const void* oat_code = method->GetOatMethodQuickCode(kRuntimePointerSize);
178 if (oat_code == nullptr) {
179 return false;
180 }
181 const void* actual_code = Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method);
182 return actual_code == oat_code;
183 }
184
GetMethod(ScopedObjectAccess & soa,jclass cls,const ScopedUtfChars & chars)185 static ArtMethod* GetMethod(ScopedObjectAccess& soa, jclass cls, const ScopedUtfChars& chars)
186 REQUIRES_SHARED(Locks::mutator_lock_) {
187 CHECK(chars.c_str() != nullptr);
188 ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
189 chars.c_str(), kRuntimePointerSize);
190 if (method == nullptr) {
191 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
192 chars.c_str(), kRuntimePointerSize);
193 }
194 DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
195 return method;
196 }
197
Java_Main_hasJitCompiledEntrypoint(JNIEnv * env,jclass,jclass cls,jstring method_name)198 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env,
199 jclass,
200 jclass cls,
201 jstring method_name) {
202 jit::Jit* jit = GetJitIfEnabled();
203 if (jit == nullptr) {
204 return false;
205 }
206 Thread* self = Thread::Current();
207 ScopedObjectAccess soa(self);
208 ScopedUtfChars chars(env, method_name);
209 ArtMethod* method = GetMethod(soa, cls, chars);
210 ScopedAssertNoThreadSuspension sants(__FUNCTION__);
211 return jit->GetCodeCache()->ContainsPc(
212 Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method));
213 }
214
Java_Main_hasJitCompiledCode(JNIEnv * env,jclass,jclass cls,jstring method_name)215 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env,
216 jclass,
217 jclass cls,
218 jstring method_name) {
219 jit::Jit* jit = GetJitIfEnabled();
220 if (jit == nullptr) {
221 return false;
222 }
223 Thread* self = Thread::Current();
224 ScopedObjectAccess soa(self);
225 ScopedUtfChars chars(env, method_name);
226 ArtMethod* method = GetMethod(soa, cls, chars);
227 return jit->GetCodeCache()->ContainsMethod(method);
228 }
229
ForceJitCompiled(Thread * self,ArtMethod * method,CompilationKind kind)230 static void ForceJitCompiled(Thread* self,
231 ArtMethod* method,
232 CompilationKind kind) REQUIRES(!Locks::mutator_lock_) {
233 // TODO(mythria): Update this check once we support method entry / exit hooks directly from
234 // JIT code instead of installing EntryExit stubs.
235 if (Runtime::Current()->GetInstrumentation()->EntryExitStubsInstalled() &&
236 (method->IsNative() || !Runtime::Current()->IsJavaDebuggable())) {
237 return;
238 }
239
240 {
241 ScopedObjectAccess soa(self);
242 if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
243 std::string msg(method->PrettyMethod());
244 msg += ": is not safe to jit!";
245 ThrowIllegalStateException(msg.c_str());
246 return;
247 }
248 // We force visible initialization of the declaring class to make sure the method
249 // doesn't keep the resolution stub as entrypoint.
250 StackHandleScope<1> hs(self);
251 Handle<mirror::Class> h_klass(hs.NewHandle(method->GetDeclaringClass()));
252 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
253 if (!class_linker->EnsureInitialized(self, h_klass, true, true)) {
254 self->AssertPendingException();
255 return;
256 }
257 if (UNLIKELY(!h_klass->IsInitialized())) {
258 // Must be initializing in this thread.
259 CHECK_EQ(h_klass->GetStatus(), ClassStatus::kInitializing);
260 CHECK_EQ(h_klass->GetClinitThreadId(), self->GetTid());
261 std::string msg(method->PrettyMethod());
262 msg += ": is not safe to jit because the class is being initialized in this thread!";
263 ThrowIllegalStateException(msg.c_str());
264 return;
265 }
266 if (!h_klass->IsVisiblyInitialized()) {
267 ScopedThreadSuspension sts(self, ThreadState::kNative);
268 class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
269 }
270 }
271 jit::Jit* jit = GetJitIfEnabled();
272 jit::JitCodeCache* code_cache = jit->GetCodeCache();
273 // Update the code cache to make sure the JIT code does not get deleted.
274 // Note: this will apply to all JIT compilations.
275 code_cache->SetGarbageCollectCode(false);
276 if (jit->JitAtFirstUse()) {
277 ScopedObjectAccess soa(self);
278 jit->CompileMethod(method, self, kind, /*prejit=*/ false);
279 } else if (kind == CompilationKind::kBaseline || jit->GetJitCompiler()->IsBaselineCompiler()) {
280 ScopedObjectAccess soa(self);
281 if (jit->TryPatternMatch(method, CompilationKind::kBaseline)) {
282 return;
283 }
284 jit->MaybeEnqueueCompilation(method, self);
285 } else {
286 jit->EnqueueOptimizedCompilation(method, self);
287 }
288 do {
289 // Sleep to yield to the compiler thread.
290 usleep(1000);
291 const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
292 if (code_cache->ContainsPc(entry_point)) {
293 // If we're running baseline or not requesting optimized, we're good to go.
294 if (jit->GetJitCompiler()->IsBaselineCompiler() || kind != CompilationKind::kOptimized) {
295 break;
296 }
297 // If we're requesting optimized, check that we did get the method
298 // compiled optimized.
299 OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(entry_point);
300 if (!CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
301 break;
302 }
303 }
304 } while (true);
305 }
306
Java_Main_ensureMethodJitCompiled(JNIEnv *,jclass,jobject meth)307 extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
308 jit::Jit* jit = GetJitIfEnabled();
309 if (jit == nullptr) {
310 return;
311 }
312
313 Thread* self = Thread::Current();
314 ArtMethod* method;
315 {
316 ScopedObjectAccess soa(self);
317 method = ArtMethod::FromReflectedMethod(soa, meth);
318 }
319 ForceJitCompiled(self, method, CompilationKind::kOptimized);
320 }
321
Java_Main_ensureJitCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)322 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
323 jclass,
324 jclass cls,
325 jstring method_name) {
326 jit::Jit* jit = GetJitIfEnabled();
327 if (jit == nullptr) {
328 return;
329 }
330
331 Thread* self = Thread::Current();
332 ArtMethod* method = nullptr;
333 {
334 ScopedObjectAccess soa(self);
335
336 ScopedUtfChars chars(env, method_name);
337 method = GetMethod(soa, cls, chars);
338 }
339 ForceJitCompiled(self, method, CompilationKind::kOptimized);
340 }
341
Java_Main_ensureJitBaselineCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)342 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitBaselineCompiled(JNIEnv* env,
343 jclass,
344 jclass cls,
345 jstring method_name) {
346 jit::Jit* jit = GetJitIfEnabled();
347 if (jit == nullptr) {
348 return;
349 }
350
351 Thread* self = Thread::Current();
352 ArtMethod* method = nullptr;
353 {
354 ScopedObjectAccess soa(self);
355
356 ScopedUtfChars chars(env, method_name);
357 method = GetMethod(soa, cls, chars);
358 }
359 ForceJitCompiled(self, method, CompilationKind::kBaseline);
360 }
361
Java_Main_hasSingleImplementation(JNIEnv * env,jclass,jclass cls,jstring method_name)362 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasSingleImplementation(JNIEnv* env,
363 jclass,
364 jclass cls,
365 jstring method_name) {
366 ArtMethod* method = nullptr;
367 ScopedObjectAccess soa(Thread::Current());
368 ScopedUtfChars chars(env, method_name);
369 CHECK(chars.c_str() != nullptr);
370 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
371 chars.c_str(), kRuntimePointerSize);
372 return method->HasSingleImplementation();
373 }
374
Java_Main_getHotnessCounter(JNIEnv * env,jclass,jclass cls,jstring method_name)375 extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
376 jclass,
377 jclass cls,
378 jstring method_name) {
379 ScopedObjectAccess soa(Thread::Current());
380 ScopedUtfChars chars(env, method_name);
381 CHECK(chars.c_str() != nullptr);
382 ArtMethod* method =
383 soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(chars.c_str(),
384 kRuntimePointerSize);
385 if (method != nullptr) {
386 return method->GetCounter();
387 }
388
389 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(chars.c_str(),
390 kRuntimePointerSize);
391 if (method != nullptr) {
392 return method->GetCounter();
393 }
394
395 return std::numeric_limits<int32_t>::min();
396 }
397
Java_Main_numberOfDeoptimizations(JNIEnv *,jclass)398 extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jclass) {
399 return Runtime::Current()->GetNumberOfDeoptimizations();
400 }
401
Java_Main_fetchProfiles(JNIEnv *,jclass)402 extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) {
403 jit::Jit* jit = GetJitIfEnabled();
404 if (jit == nullptr) {
405 return;
406 }
407 jit::JitCodeCache* code_cache = jit->GetCodeCache();
408 std::vector<ProfileMethodInfo> unused_vector;
409 std::set<std::string> unused_locations;
410 unused_locations.insert("fake_location");
411 ScopedObjectAccess soa(Thread::Current());
412 code_cache->GetProfiledMethods(unused_locations, unused_vector, /*inline_cache_threshold=*/0);
413 }
414
Java_Main_waitForCompilation(JNIEnv *,jclass)415 extern "C" JNIEXPORT void JNICALL Java_Main_waitForCompilation(JNIEnv*, jclass) {
416 jit::Jit* jit = Runtime::Current()->GetJit();
417 if (jit != nullptr) {
418 jit->WaitForCompilationToFinish(Thread::Current());
419 }
420 }
421
Java_Main_stopJit(JNIEnv *,jclass)422 extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
423 jit::Jit* jit = Runtime::Current()->GetJit();
424 if (jit != nullptr) {
425 jit->Stop();
426 }
427 }
428
Java_Main_startJit(JNIEnv *,jclass)429 extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
430 jit::Jit* jit = Runtime::Current()->GetJit();
431 if (jit != nullptr) {
432 jit->Start();
433 }
434 }
435
Java_Main_getJitThreshold(JNIEnv *,jclass)436 extern "C" JNIEXPORT jint JNICALL Java_Main_getJitThreshold(JNIEnv*, jclass) {
437 jit::Jit* jit = Runtime::Current()->GetJit();
438 return (jit != nullptr) ? jit->HotMethodThreshold() : 0;
439 }
440
Java_Main_deoptimizeBootImage(JNIEnv *,jclass)441 extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeBootImage(JNIEnv*, jclass) {
442 ScopedSuspendAll ssa(__FUNCTION__);
443 Runtime::Current()->DeoptimizeBootImage();
444 }
445
Java_Main_deoptimizeNativeMethod(JNIEnv * env,jclass,jclass cls,jstring method_name)446 extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeNativeMethod(JNIEnv* env,
447 jclass,
448 jclass cls,
449 jstring method_name) {
450 Thread* self = Thread::Current();
451 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
452 // Make initialized classes visibly initialized to avoid entrypoint being set to boot JNI stub
453 // after deoptimize.
454 class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
455 ScopedObjectAccess soa(self);
456 ScopedUtfChars chars(env, method_name);
457 ArtMethod* method = GetMethod(soa, cls, chars);
458 CHECK(method->IsNative());
459 Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
460 }
461
Java_Main_isDebuggable(JNIEnv *,jclass)462 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDebuggable(JNIEnv*, jclass) {
463 return Runtime::Current()->IsJavaDebuggable() ? JNI_TRUE : JNI_FALSE;
464 }
465
Java_Main_setTargetSdkVersion(JNIEnv *,jclass,jint version)466 extern "C" JNIEXPORT void JNICALL Java_Main_setTargetSdkVersion(JNIEnv*, jclass, jint version) {
467 Runtime::Current()->SetTargetSdkVersion(static_cast<uint32_t>(version));
468 }
469
Java_Main_genericFieldOffset(JNIEnv * env,jclass,jobject fld)470 extern "C" JNIEXPORT jlong JNICALL Java_Main_genericFieldOffset(JNIEnv* env, jclass, jobject fld) {
471 jfieldID fid = env->FromReflectedField(fld);
472 ScopedObjectAccess soa(env);
473 ArtField* af = jni::DecodeArtField(fid);
474 return af->GetOffset().Int32Value();
475 }
476
Java_Main_isObsoleteObject(JNIEnv * env,jclass,jclass c)477 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isObsoleteObject(JNIEnv* env, jclass, jclass c) {
478 ScopedObjectAccess soa(env);
479 return soa.Decode<mirror::Class>(c)->IsObsoleteObject();
480 }
481
Java_Main_forceInterpreterOnThread(JNIEnv * env,jclass cls)482 extern "C" JNIEXPORT void JNICALL Java_Main_forceInterpreterOnThread(JNIEnv* env,
483 [[maybe_unused]] jclass cls) {
484 ScopedObjectAccess soa(env);
485 MutexLock thread_list_mu(soa.Self(), *Locks::thread_list_lock_);
486 soa.Self()->IncrementForceInterpreterCount();
487 }
488
Java_Main_setAsyncExceptionsThrown(JNIEnv * env,jclass cls)489 extern "C" JNIEXPORT void JNICALL Java_Main_setAsyncExceptionsThrown([[maybe_unused]] JNIEnv* env,
490 [[maybe_unused]] jclass cls) {
491 Runtime::Current()->SetAsyncExceptionsThrown();
492 }
493
Java_Main_setRlimitNoFile(JNIEnv *,jclass,jint value)494 extern "C" JNIEXPORT void JNICALL Java_Main_setRlimitNoFile(JNIEnv*, jclass, jint value) {
495 rlimit limit { static_cast<rlim_t>(value), static_cast<rlim_t>(value) };
496 setrlimit(RLIMIT_NOFILE, &limit);
497 }
498
Java_Main_isInImageSpace(JNIEnv * env,jclass caller,jclass cls)499 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInImageSpace(JNIEnv* env,
500 [[maybe_unused]] jclass caller,
501 jclass cls) {
502 ScopedObjectAccess soa(env);
503
504 ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
505 gc::space::Space* space =
506 Runtime::Current()->GetHeap()->FindSpaceFromObject(klass, /*fail_ok=*/true);
507 if (space == nullptr) {
508 return JNI_FALSE;
509 }
510 return space->IsImageSpace() ? JNI_TRUE : JNI_FALSE;
511 }
512
513 // Ensures the profile saver does its usual processing.
Java_Main_ensureProfileProcessing(JNIEnv *,jclass)514 extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
515 ProfileSaver::ForceProcessProfiles();
516 }
517
Java_Main_isForBootImage(JNIEnv * env,jclass,jstring filename)518 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isForBootImage(JNIEnv* env,
519 jclass,
520 jstring filename) {
521 ScopedUtfChars filename_chars(env, filename);
522 CHECK(filename_chars.c_str() != nullptr);
523
524 ProfileCompilationInfo info(/*for_boot_image=*/true);
525 bool result = info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/false);
526 return result ? JNI_TRUE : JNI_FALSE;
527 }
528
GetMethodHotnessFromProfile(JNIEnv * env,jclass c,jstring filename,jobject method)529 static ProfileCompilationInfo::MethodHotness GetMethodHotnessFromProfile(JNIEnv* env,
530 jclass c,
531 jstring filename,
532 jobject method) {
533 bool for_boot_image = Java_Main_isForBootImage(env, c, filename) == JNI_TRUE;
534 ScopedUtfChars filename_chars(env, filename);
535 CHECK(filename_chars.c_str() != nullptr);
536 ScopedObjectAccess soa(env);
537 ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
538 ArtMethod* art_method = exec->GetArtMethod();
539 MethodReference ref(art_method->GetDexFile(), art_method->GetDexMethodIndex());
540
541 ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(), for_boot_image);
542 if (!info.Load(filename_chars.c_str(), /*clear_if_invalid=*/false)) {
543 LOG(ERROR) << "Failed to load profile from " << filename;
544 return ProfileCompilationInfo::MethodHotness();
545 }
546 return info.GetMethodHotness(ref);
547 }
548
549 // Checks if the method is present in the profile.
Java_Main_presentInProfile(JNIEnv * env,jclass c,jstring filename,jobject method)550 extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
551 jclass c,
552 jstring filename,
553 jobject method) {
554 // TODO: Why do we check `hotness.IsHot()` instead of `hotness.IsInProfile()`
555 // in a method named `presentInProfile()`?
556 return GetMethodHotnessFromProfile(env, c, filename, method).IsHot() ? JNI_TRUE : JNI_FALSE;
557 }
558
559 // Checks if the method has an inline cache in the profile that contains at least the given target
560 // types.
Java_Main_hasInlineCacheInProfile(JNIEnv * env,jclass c,jstring filename,jobject method,jobjectArray target_types)561 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasInlineCacheInProfile(
562 JNIEnv* env, jclass c, jstring filename, jobject method, jobjectArray target_types) {
563 ProfileCompilationInfo::MethodHotness hotness =
564 GetMethodHotnessFromProfile(env, c, filename, method);
565 if (hotness.GetInlineCacheMap() == nullptr) {
566 return JNI_FALSE;
567 }
568 ScopedObjectAccess soa(env);
569 ObjPtr<mirror::ObjectArray<mirror::Class>> types =
570 soa.Decode<mirror::ObjectArray<mirror::Class>>(target_types);
571 for (const auto& [dex_pc, dex_pc_data] : *hotness.GetInlineCacheMap()) {
572 bool match = true;
573 for (ObjPtr<mirror::Class> type : *types.Ptr()) {
574 dex::TypeIndex expected_index = type->GetDexTypeIndex();
575 if (!expected_index.IsValid()) {
576 return JNI_FALSE;
577 }
578 if (dex_pc_data.classes.find(expected_index) == dex_pc_data.classes.end()) {
579 match = false;
580 break;
581 }
582 }
583 if (match) {
584 return JNI_TRUE;
585 }
586 }
587 return JNI_FALSE;
588 }
589
Java_Main_getCurrentGcNum(JNIEnv * env,jclass)590 extern "C" JNIEXPORT jint JNICALL Java_Main_getCurrentGcNum(JNIEnv* env, jclass) {
591 // Prevent any new GC before getting the current GC num.
592 ScopedObjectAccess soa(env);
593 gc::Heap* heap = Runtime::Current()->GetHeap();
594 heap->WaitForGcToComplete(gc::kGcCauseJitCodeCache, Thread::Current());
595 return heap->GetCurrentGcNum();
596 }
597
Java_Main_removeJitCompiledMethod(JNIEnv * env,jclass,jobject java_method,jboolean release_memory)598 extern "C" JNIEXPORT jboolean Java_Main_removeJitCompiledMethod(JNIEnv* env,
599 jclass,
600 jobject java_method,
601 jboolean release_memory) {
602 if (!Runtime::Current()->UseJitCompilation()) {
603 return JNI_FALSE;
604 }
605
606 jit::Jit* jit = Runtime::Current()->GetJit();
607 jit->WaitForCompilationToFinish(Thread::Current());
608
609 ScopedObjectAccess soa(env);
610 ArtMethod* method = ArtMethod::FromReflectedMethod(soa, java_method);
611
612 jit::JitCodeCache* code_cache = jit->GetCodeCache();
613
614 // Drop the shared mutator lock.
615 ScopedThreadSuspension self_suspension(Thread::Current(), art::ThreadState::kNative);
616 // Get exclusive mutator lock with suspend all.
617 ScopedSuspendAll suspend("Removing JIT compiled method", /*long_suspend=*/true);
618 bool removed = code_cache->RemoveMethod(method, static_cast<bool>(release_memory));
619 return removed ? JNI_TRUE : JNI_FALSE;
620 }
621
622 } // namespace art
623