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 return;
280 }
281 if (kind == CompilationKind::kBaseline || jit->GetJitCompiler()->IsBaselineCompiler()) {
282 ScopedObjectAccess soa(self);
283 if (jit->TryPatternMatch(method, CompilationKind::kBaseline)) {
284 return;
285 }
286 jit->MaybeEnqueueCompilation(method, self);
287 } else {
288 jit->EnqueueOptimizedCompilation(method, self);
289 }
290 do {
291 // Sleep to yield to the compiler thread.
292 usleep(1000);
293 const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
294 if (code_cache->ContainsPc(entry_point)) {
295 // If we're running baseline or not requesting optimized, we're good to go.
296 if (jit->GetJitCompiler()->IsBaselineCompiler() || kind != CompilationKind::kOptimized) {
297 break;
298 }
299 // If we're requesting optimized, check that we did get the method
300 // compiled optimized.
301 OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(entry_point);
302 if (!CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
303 break;
304 }
305 }
306 } while (true);
307 }
308
Java_Main_ensureMethodJitCompiled(JNIEnv *,jclass,jobject meth)309 extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
310 jit::Jit* jit = GetJitIfEnabled();
311 if (jit == nullptr) {
312 return;
313 }
314
315 Thread* self = Thread::Current();
316 ArtMethod* method;
317 {
318 ScopedObjectAccess soa(self);
319 method = ArtMethod::FromReflectedMethod(soa, meth);
320 }
321 ForceJitCompiled(self, method, CompilationKind::kOptimized);
322 }
323
Java_Main_ensureJitCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)324 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
325 jclass,
326 jclass cls,
327 jstring method_name) {
328 jit::Jit* jit = GetJitIfEnabled();
329 if (jit == nullptr) {
330 return;
331 }
332
333 Thread* self = Thread::Current();
334 ArtMethod* method = nullptr;
335 {
336 ScopedObjectAccess soa(self);
337
338 ScopedUtfChars chars(env, method_name);
339 method = GetMethod(soa, cls, chars);
340 }
341 ForceJitCompiled(self, method, CompilationKind::kOptimized);
342 }
343
Java_Main_ensureJitBaselineCompiled(JNIEnv * env,jclass,jclass cls,jstring method_name)344 extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitBaselineCompiled(JNIEnv* env,
345 jclass,
346 jclass cls,
347 jstring method_name) {
348 jit::Jit* jit = GetJitIfEnabled();
349 if (jit == nullptr) {
350 return;
351 }
352
353 Thread* self = Thread::Current();
354 ArtMethod* method = nullptr;
355 {
356 ScopedObjectAccess soa(self);
357
358 ScopedUtfChars chars(env, method_name);
359 method = GetMethod(soa, cls, chars);
360 }
361 ForceJitCompiled(self, method, CompilationKind::kBaseline);
362 }
363
Java_Main_hasSingleImplementation(JNIEnv * env,jclass,jclass cls,jstring method_name)364 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasSingleImplementation(JNIEnv* env,
365 jclass,
366 jclass cls,
367 jstring method_name) {
368 ArtMethod* method = nullptr;
369 ScopedObjectAccess soa(Thread::Current());
370 ScopedUtfChars chars(env, method_name);
371 CHECK(chars.c_str() != nullptr);
372 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
373 chars.c_str(), kRuntimePointerSize);
374 return method->HasSingleImplementation();
375 }
376
Java_Main_getHotnessCounter(JNIEnv * env,jclass,jclass cls,jstring method_name)377 extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
378 jclass,
379 jclass cls,
380 jstring method_name) {
381 ScopedObjectAccess soa(Thread::Current());
382 ScopedUtfChars chars(env, method_name);
383 CHECK(chars.c_str() != nullptr);
384 ArtMethod* method =
385 soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(chars.c_str(),
386 kRuntimePointerSize);
387 if (method != nullptr) {
388 return method->GetCounter();
389 }
390
391 method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(chars.c_str(),
392 kRuntimePointerSize);
393 if (method != nullptr) {
394 return method->GetCounter();
395 }
396
397 return std::numeric_limits<int32_t>::min();
398 }
399
Java_Main_numberOfDeoptimizations(JNIEnv *,jclass)400 extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jclass) {
401 return Runtime::Current()->GetNumberOfDeoptimizations();
402 }
403
Java_Main_fetchProfiles(JNIEnv *,jclass)404 extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) {
405 jit::Jit* jit = GetJitIfEnabled();
406 if (jit == nullptr) {
407 return;
408 }
409 jit::JitCodeCache* code_cache = jit->GetCodeCache();
410 std::vector<ProfileMethodInfo> unused_vector;
411 std::set<std::string> unused_locations;
412 unused_locations.insert("fake_location");
413 ScopedObjectAccess soa(Thread::Current());
414 code_cache->GetProfiledMethods(unused_locations, unused_vector, /*inline_cache_threshold=*/0);
415 }
416
Java_Main_waitForCompilation(JNIEnv *,jclass)417 extern "C" JNIEXPORT void JNICALL Java_Main_waitForCompilation(JNIEnv*, jclass) {
418 jit::Jit* jit = Runtime::Current()->GetJit();
419 if (jit != nullptr) {
420 jit->WaitForCompilationToFinish(Thread::Current());
421 }
422 }
423
Java_Main_stopJit(JNIEnv *,jclass)424 extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
425 jit::Jit* jit = Runtime::Current()->GetJit();
426 if (jit != nullptr) {
427 jit->Stop();
428 }
429 }
430
Java_Main_startJit(JNIEnv *,jclass)431 extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
432 jit::Jit* jit = Runtime::Current()->GetJit();
433 if (jit != nullptr) {
434 jit->Start();
435 }
436 }
437
Java_Main_getJitThreshold(JNIEnv *,jclass)438 extern "C" JNIEXPORT jint JNICALL Java_Main_getJitThreshold(JNIEnv*, jclass) {
439 jit::Jit* jit = Runtime::Current()->GetJit();
440 return (jit != nullptr) ? jit->HotMethodThreshold() : 0;
441 }
442
Java_Main_deoptimizeBootImage(JNIEnv *,jclass)443 extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeBootImage(JNIEnv*, jclass) {
444 ScopedSuspendAll ssa(__FUNCTION__);
445 Runtime::Current()->DeoptimizeBootImage();
446 }
447
Java_Main_deoptimizeNativeMethod(JNIEnv * env,jclass,jclass cls,jstring method_name)448 extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeNativeMethod(JNIEnv* env,
449 jclass,
450 jclass cls,
451 jstring method_name) {
452 Thread* self = Thread::Current();
453 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
454 // Make initialized classes visibly initialized to avoid entrypoint being set to boot JNI stub
455 // after deoptimize.
456 class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
457 ScopedObjectAccess soa(self);
458 ScopedUtfChars chars(env, method_name);
459 ArtMethod* method = GetMethod(soa, cls, chars);
460 CHECK(method->IsNative());
461 Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
462 }
463
Java_Main_isDebuggable(JNIEnv *,jclass)464 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDebuggable(JNIEnv*, jclass) {
465 return Runtime::Current()->IsJavaDebuggable() ? JNI_TRUE : JNI_FALSE;
466 }
467
Java_Main_setTargetSdkVersion(JNIEnv *,jclass,jint version)468 extern "C" JNIEXPORT void JNICALL Java_Main_setTargetSdkVersion(JNIEnv*, jclass, jint version) {
469 Runtime::Current()->SetTargetSdkVersion(static_cast<uint32_t>(version));
470 }
471
Java_Main_genericFieldOffset(JNIEnv * env,jclass,jobject fld)472 extern "C" JNIEXPORT jlong JNICALL Java_Main_genericFieldOffset(JNIEnv* env, jclass, jobject fld) {
473 jfieldID fid = env->FromReflectedField(fld);
474 ScopedObjectAccess soa(env);
475 ArtField* af = jni::DecodeArtField(fid);
476 return af->GetOffset().Int32Value();
477 }
478
Java_Main_isObsoleteObject(JNIEnv * env,jclass,jclass c)479 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isObsoleteObject(JNIEnv* env, jclass, jclass c) {
480 ScopedObjectAccess soa(env);
481 return soa.Decode<mirror::Class>(c)->IsObsoleteObject();
482 }
483
Java_Main_forceInterpreterOnThread(JNIEnv * env,jclass cls)484 extern "C" JNIEXPORT void JNICALL Java_Main_forceInterpreterOnThread(JNIEnv* env,
485 [[maybe_unused]] jclass cls) {
486 ScopedObjectAccess soa(env);
487 MutexLock thread_list_mu(soa.Self(), *Locks::thread_list_lock_);
488 soa.Self()->IncrementForceInterpreterCount();
489 }
490
Java_Main_setAsyncExceptionsThrown(JNIEnv * env,jclass cls)491 extern "C" JNIEXPORT void JNICALL Java_Main_setAsyncExceptionsThrown([[maybe_unused]] JNIEnv* env,
492 [[maybe_unused]] jclass cls) {
493 Runtime::Current()->SetAsyncExceptionsThrown();
494 }
495
Java_Main_setRlimitNoFile(JNIEnv *,jclass,jint value)496 extern "C" JNIEXPORT void JNICALL Java_Main_setRlimitNoFile(JNIEnv*, jclass, jint value) {
497 rlimit limit { static_cast<rlim_t>(value), static_cast<rlim_t>(value) };
498 setrlimit(RLIMIT_NOFILE, &limit);
499 }
500
Java_Main_isInImageSpace(JNIEnv * env,jclass caller,jclass cls)501 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInImageSpace(JNIEnv* env,
502 [[maybe_unused]] jclass caller,
503 jclass cls) {
504 ScopedObjectAccess soa(env);
505
506 ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
507 gc::space::Space* space =
508 Runtime::Current()->GetHeap()->FindSpaceFromObject(klass, /*fail_ok=*/true);
509 if (space == nullptr) {
510 return JNI_FALSE;
511 }
512 return space->IsImageSpace() ? JNI_TRUE : JNI_FALSE;
513 }
514
515 // Ensures the profile saver does its usual processing.
Java_Main_ensureProfileProcessing(JNIEnv *,jclass)516 extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
517 ProfileSaver::ForceProcessProfiles();
518 }
519
Java_Main_isForBootImage(JNIEnv * env,jclass,jstring filename)520 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isForBootImage(JNIEnv* env,
521 jclass,
522 jstring filename) {
523 ScopedUtfChars filename_chars(env, filename);
524 CHECK(filename_chars.c_str() != nullptr);
525
526 ProfileCompilationInfo info(/*for_boot_image=*/true);
527 bool result = info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/false);
528 return result ? JNI_TRUE : JNI_FALSE;
529 }
530
GetMethodHotnessFromProfile(JNIEnv * env,jclass c,jstring filename,jobject method)531 static ProfileCompilationInfo::MethodHotness GetMethodHotnessFromProfile(JNIEnv* env,
532 jclass c,
533 jstring filename,
534 jobject method) {
535 bool for_boot_image = Java_Main_isForBootImage(env, c, filename) == JNI_TRUE;
536 ScopedUtfChars filename_chars(env, filename);
537 CHECK(filename_chars.c_str() != nullptr);
538 ScopedObjectAccess soa(env);
539 ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
540 ArtMethod* art_method = exec->GetArtMethod();
541 MethodReference ref(art_method->GetDexFile(), art_method->GetDexMethodIndex());
542
543 ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(), for_boot_image);
544 if (!info.Load(filename_chars.c_str(), /*clear_if_invalid=*/false)) {
545 LOG(ERROR) << "Failed to load profile from " << filename;
546 return ProfileCompilationInfo::MethodHotness();
547 }
548 return info.GetMethodHotness(ref);
549 }
550
551 // Checks if the method is present in the profile.
Java_Main_presentInProfile(JNIEnv * env,jclass c,jstring filename,jobject method)552 extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
553 jclass c,
554 jstring filename,
555 jobject method) {
556 // TODO: Why do we check `hotness.IsHot()` instead of `hotness.IsInProfile()`
557 // in a method named `presentInProfile()`?
558 return GetMethodHotnessFromProfile(env, c, filename, method).IsHot() ? JNI_TRUE : JNI_FALSE;
559 }
560
561 // Checks if the method has an inline cache in the profile that contains at least the given target
562 // types.
Java_Main_hasInlineCacheInProfile(JNIEnv * env,jclass c,jstring filename,jobject method,jobjectArray target_types)563 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasInlineCacheInProfile(
564 JNIEnv* env, jclass c, jstring filename, jobject method, jobjectArray target_types) {
565 ProfileCompilationInfo::MethodHotness hotness =
566 GetMethodHotnessFromProfile(env, c, filename, method);
567 if (hotness.GetInlineCacheMap() == nullptr) {
568 return JNI_FALSE;
569 }
570 ScopedObjectAccess soa(env);
571 ObjPtr<mirror::ObjectArray<mirror::Class>> types =
572 soa.Decode<mirror::ObjectArray<mirror::Class>>(target_types);
573 for (const auto& [dex_pc, dex_pc_data] : *hotness.GetInlineCacheMap()) {
574 bool match = true;
575 for (ObjPtr<mirror::Class> type : *types.Ptr()) {
576 dex::TypeIndex expected_index = type->GetDexTypeIndex();
577 if (!expected_index.IsValid()) {
578 return JNI_FALSE;
579 }
580 if (dex_pc_data.classes.find(expected_index) == dex_pc_data.classes.end()) {
581 match = false;
582 break;
583 }
584 }
585 if (match) {
586 return JNI_TRUE;
587 }
588 }
589 return JNI_FALSE;
590 }
591
Java_Main_getCurrentGcNum(JNIEnv * env,jclass)592 extern "C" JNIEXPORT jint JNICALL Java_Main_getCurrentGcNum(JNIEnv* env, jclass) {
593 // Prevent any new GC before getting the current GC num.
594 ScopedObjectAccess soa(env);
595 gc::Heap* heap = Runtime::Current()->GetHeap();
596 heap->WaitForGcToComplete(gc::kGcCauseJitCodeCache, Thread::Current());
597 return heap->GetCurrentGcNum();
598 }
599
Java_Main_removeJitCompiledMethod(JNIEnv * env,jclass,jobject java_method,jboolean release_memory)600 extern "C" JNIEXPORT jboolean Java_Main_removeJitCompiledMethod(JNIEnv* env,
601 jclass,
602 jobject java_method,
603 jboolean release_memory) {
604 if (!Runtime::Current()->UseJitCompilation()) {
605 return JNI_FALSE;
606 }
607
608 jit::Jit* jit = Runtime::Current()->GetJit();
609 jit->WaitForCompilationToFinish(Thread::Current());
610
611 ScopedObjectAccess soa(env);
612 ArtMethod* method = ArtMethod::FromReflectedMethod(soa, java_method);
613
614 jit::JitCodeCache* code_cache = jit->GetCodeCache();
615
616 // Drop the shared mutator lock.
617 ScopedThreadSuspension self_suspension(Thread::Current(), art::ThreadState::kNative);
618 // Get exclusive mutator lock with suspend all.
619 ScopedSuspendAll suspend("Removing JIT compiled method", /*long_suspend=*/true);
620 bool removed = code_cache->RemoveMethod(method, static_cast<bool>(release_memory));
621 return removed ? JNI_TRUE : JNI_FALSE;
622 }
623
624 } // namespace art
625