/* * Copyright (C) 2008 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 "dalvik_system_VMRuntime.h" #ifdef ART_TARGET_ANDROID #include #include extern "C" void android_set_application_target_sdk_version(uint32_t version); #endif #include #include #include #include "nativehelper/scoped_utf_chars.h" #include #include #include "arch/instruction_set.h" #include "art_method-inl.h" #include "base/enums.h" #include "base/sdk_version.h" #include "class_linker-inl.h" #include "class_loader_context.h" #include "common_throws.h" #include "debugger.h" #include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocator/dlmalloc.h" #include "gc/heap.h" #include "gc/space/dlmalloc_space.h" #include "gc/space/image_space.h" #include "gc/task_processor.h" #include "intern_table.h" #include "jit/jit.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" #include "mirror/array-alloc-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" #include "runtime.h" #include "scoped_fast_native_object_access-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" #include "thread_list.h" #include "well_known_classes.h" namespace art { using android::base::StringPrintf; static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) { return Runtime::Current()->GetHeap()->GetTargetHeapUtilization(); } static void VMRuntime_nativeSetTargetHeapUtilization(JNIEnv*, jobject, jfloat target) { Runtime::Current()->GetHeap()->SetTargetHeapUtilization(target); } static void VMRuntime_startJitCompilation(JNIEnv*, jobject) { } static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { } static void VMRuntime_setHiddenApiExemptions(JNIEnv* env, jclass, jobjectArray exemptions) { std::vector exemptions_vec; int exemptions_length = env->GetArrayLength(exemptions); for (int i = 0; i < exemptions_length; i++) { jstring exemption = reinterpret_cast(env->GetObjectArrayElement(exemptions, i)); const char* raw_exemption = env->GetStringUTFChars(exemption, nullptr); exemptions_vec.push_back(raw_exemption); env->ReleaseStringUTFChars(exemption, raw_exemption); } Runtime::Current()->SetHiddenApiExemptions(exemptions_vec); } static void VMRuntime_setHiddenApiAccessLogSamplingRate(JNIEnv*, jclass, jint rate) { Runtime::Current()->SetHiddenApiEventLogSampleRate(rate); } static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); if (UNLIKELY(length < 0)) { ThrowNegativeArraySizeException(length); return nullptr; } ObjPtr element_class = soa.Decode(javaElementClass); if (UNLIKELY(element_class == nullptr)) { ThrowNullPointerException("element class == null"); return nullptr; } Runtime* runtime = Runtime::Current(); ObjPtr array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator(); ObjPtr result = mirror::Array::Alloc(soa.Self(), array_class, length, array_class->GetComponentSizeShift(), allocator); return soa.AddLocalReference(result); } static jobject VMRuntime_newUnpaddedArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); if (UNLIKELY(length < 0)) { ThrowNegativeArraySizeException(length); return nullptr; } ObjPtr element_class = soa.Decode(javaElementClass); if (UNLIKELY(element_class == nullptr)) { ThrowNullPointerException("element class == null"); return nullptr; } Runtime* runtime = Runtime::Current(); ObjPtr array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); ObjPtr result = mirror::Array::Alloc( soa.Self(), array_class, length, array_class->GetComponentSizeShift(), allocator); return soa.AddLocalReference(result); } static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { if (javaArray == nullptr) { // Most likely allocation failed return 0; } ScopedFastNativeObjectAccess soa(env); ObjPtr array = soa.Decode(javaArray); if (!array->IsArrayInstance()) { ThrowIllegalArgumentException("not an array"); return 0; } if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { ThrowRuntimeException("Trying to get address of movable array object"); return 0; } return reinterpret_cast(array->GetRawData(array->GetClass()->GetComponentSize(), 0)); } static void VMRuntime_clearGrowthLimit(JNIEnv*, jobject) { Runtime::Current()->GetHeap()->ClearGrowthLimit(); } static void VMRuntime_clampGrowthLimit(JNIEnv*, jobject) { Runtime::Current()->GetHeap()->ClampGrowthLimit(); } static jboolean VMRuntime_isNativeDebuggable(JNIEnv*, jobject) { return Runtime::Current()->IsNativeDebuggable(); } static jboolean VMRuntime_isJavaDebuggable(JNIEnv*, jobject) { return Runtime::Current()->IsJavaDebuggable(); } static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) { DCHECK(WellKnownClasses::java_lang_String != nullptr); const std::vector& properties = Runtime::Current()->GetProperties(); ScopedLocalRef ret(env, env->NewObjectArray(static_cast(properties.size()), WellKnownClasses::java_lang_String, nullptr /* initial element */)); if (ret == nullptr) { DCHECK(env->ExceptionCheck()); return nullptr; } for (size_t i = 0; i != properties.size(); ++i) { ScopedLocalRef str(env, env->NewStringUTF(properties[i].c_str())); if (str == nullptr) { DCHECK(env->ExceptionCheck()); return nullptr; } env->SetObjectArrayElement(ret.get(), static_cast(i), str.get()); DCHECK(!env->ExceptionCheck()); } return ret.release(); } // This is for backward compatibility with dalvik which returned the // meaningless "." when no boot classpath or classpath was // specified. Unfortunately, some tests were using java.class.path to // lookup relative file locations, so they are counting on this to be // ".", presumably some applications or libraries could have as well. static const char* DefaultToDot(const std::string& class_path) { return class_path.empty() ? "." : class_path.c_str(); } static jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) { std::string boot_class_path = android::base::Join(Runtime::Current()->GetBootClassPath(), ':'); return env->NewStringUTF(DefaultToDot(boot_class_path)); } static jstring VMRuntime_classPath(JNIEnv* env, jobject) { return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetClassPathString())); } static jstring VMRuntime_vmVersion(JNIEnv* env, jobject) { return env->NewStringUTF(Runtime::GetVersion()); } static jstring VMRuntime_vmLibrary(JNIEnv* env, jobject) { return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so"); } static jstring VMRuntime_vmInstructionSet(JNIEnv* env, jobject) { InstructionSet isa = Runtime::Current()->GetInstructionSet(); const char* isa_string = GetInstructionSetString(isa); return env->NewStringUTF(isa_string); } static jboolean VMRuntime_is64Bit(JNIEnv*, jobject) { bool is64BitMode = (sizeof(void*) == sizeof(uint64_t)); return is64BitMode ? JNI_TRUE : JNI_FALSE; } static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) { return down_cast(env)->GetVm()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE; } static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) { // This is the target SDK version of the app we're about to run. It is intended that this a place // where workarounds can be enabled. // Note that targetSdkVersion may be CUR_DEVELOPMENT (10000). // Note that targetSdkVersion may be 0, meaning "current". uint32_t uint_target_sdk_version = target_sdk_version <= 0 ? static_cast(SdkVersion::kUnset) : static_cast(target_sdk_version); Runtime::Current()->SetTargetSdkVersion(uint_target_sdk_version); #ifdef ART_TARGET_ANDROID // This part is letting libc/dynamic linker know about current app's // target sdk version to enable compatibility workarounds. android_set_application_target_sdk_version(uint_target_sdk_version); #endif } static void VMRuntime_setDisabledCompatChangesNative(JNIEnv* env, jobject, jlongArray disabled_compat_changes) { if (disabled_compat_changes == nullptr) { return; } std::set disabled_compat_changes_set; int length = env->GetArrayLength(disabled_compat_changes); jlong* elements = env->GetLongArrayElements(disabled_compat_changes, /*isCopy*/nullptr); for (int i = 0; i < length; i++) { disabled_compat_changes_set.insert(static_cast(elements[i])); } Runtime::Current()->SetDisabledCompatChanges(disabled_compat_changes_set); } static inline size_t clamp_to_size_t(jlong n) { if (sizeof(jlong) > sizeof(size_t) && UNLIKELY(n > static_cast(std::numeric_limits::max()))) { return std::numeric_limits::max(); } else { return n; } } static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jlong bytes) { if (UNLIKELY(bytes < 0)) { ScopedObjectAccess soa(env); ThrowRuntimeException("allocation size negative %" PRId64, bytes); return; } Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, clamp_to_size_t(bytes)); } static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jlong bytes) { if (UNLIKELY(bytes < 0)) { ScopedObjectAccess soa(env); ThrowRuntimeException("allocation size negative %" PRId64, bytes); return; } Runtime::Current()->GetHeap()->RegisterNativeFree(env, clamp_to_size_t(bytes)); } static jint VMRuntime_getNotifyNativeInterval(JNIEnv*, jclass) { return Runtime::Current()->GetHeap()->GetNotifyNativeInterval(); } static void VMRuntime_notifyNativeAllocationsInternal(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->NotifyNativeAllocations(env); } static jlong VMRuntime_getFinalizerTimeoutMs(JNIEnv*, jobject) { return Runtime::Current()->GetFinalizerTimeoutMs(); } static void VMRuntime_registerSensitiveThread(JNIEnv*, jobject) { Runtime::Current()->RegisterSensitiveThread(); } static void VMRuntime_updateProcessState(JNIEnv*, jobject, jint process_state) { Runtime* runtime = Runtime::Current(); runtime->UpdateProcessState(static_cast(process_state)); } static void VMRuntime_notifyStartupCompleted(JNIEnv*, jobject) { Runtime::Current()->NotifyStartupCompleted(); } static void VMRuntime_trimHeap(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->Trim(ThreadForEnv(env)); } static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->ConcurrentGC(ThreadForEnv(env), gc::kGcCauseBackground, true); } static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->RequestTrim(ThreadForEnv(env)); } static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->RequestConcurrentGC(ThreadForEnv(env), gc::kGcCauseBackground, true); } static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(ThreadForEnv(env)); } static void VMRuntime_stopHeapTaskProcessor(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(ThreadForEnv(env)); } static void VMRuntime_runHeapTasks(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env)); } using StringTable = std::map>; class PreloadDexCachesStringsVisitor : public SingleRootVisitor { public: explicit PreloadDexCachesStringsVisitor(StringTable* table) : table_(table) { } void VisitRoot(mirror::Object* root, const RootInfo& info ATTRIBUTE_UNUSED) override REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr string = root->AsString(); table_->operator[](string->ToModifiedUtf8()) = string; } private: StringTable* const table_; }; // Based on ClassLinker::ResolveString. static void PreloadDexCachesResolveString( ObjPtr dex_cache, dex::StringIndex string_idx, StringTable& strings) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx); auto pair = dex_cache->GetStrings()[slot_idx].load(std::memory_order_relaxed); if (!pair.object.IsNull()) { return; // The entry already contains some String. } const DexFile* dex_file = dex_cache->GetDexFile(); const char* utf8 = dex_file->StringDataByIdx(string_idx); ObjPtr string = strings[utf8]; if (string == nullptr) { return; } dex_cache->SetResolvedString(string_idx, string); } // Based on ClassLinker::ResolveType. static void PreloadDexCachesResolveType(Thread* self, ObjPtr dex_cache, dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t slot_idx = dex_cache->TypeSlotIndex(type_idx); auto pair = dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed); if (!pair.object.IsNull()) { return; // The entry already contains some Class. } const DexFile* dex_file = dex_cache->GetDexFile(); const char* class_name = dex_file->StringByTypeIdx(type_idx); ClassLinker* linker = Runtime::Current()->GetClassLinker(); ObjPtr klass = (class_name[1] == '\0') ? linker->LookupPrimitiveClass(class_name[0]) : linker->LookupClass(self, class_name, nullptr); if (klass == nullptr || !klass->IsResolved()) { return; } dex_cache->SetResolvedType(type_idx, klass); } // Based on ClassLinker::ResolveField. static void PreloadDexCachesResolveField(ObjPtr dex_cache, uint32_t field_idx, bool is_static) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t slot_idx = dex_cache->FieldSlotIndex(field_idx); auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedFields(), slot_idx, kRuntimePointerSize); if (pair.object != nullptr) { return; // The entry already contains some ArtField. } const DexFile* dex_file = dex_cache->GetDexFile(); const dex::FieldId& field_id = dex_file->GetFieldId(field_idx); ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( field_id.class_idx_, dex_cache, /* class_loader= */ nullptr); if (klass == nullptr) { return; } ArtField* field = is_static ? mirror::Class::FindStaticField(Thread::Current(), klass, dex_cache, field_idx) : klass->FindInstanceField(dex_cache, field_idx); if (field == nullptr) { return; } dex_cache->SetResolvedField(field_idx, field, kRuntimePointerSize); } // Based on ClassLinker::ResolveMethod. static void PreloadDexCachesResolveMethod(ObjPtr dex_cache, uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t slot_idx = dex_cache->MethodSlotIndex(method_idx); auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedMethods(), slot_idx, kRuntimePointerSize); if (pair.object != nullptr) { return; // The entry already contains some ArtMethod. } const DexFile* dex_file = dex_cache->GetDexFile(); const dex::MethodId& method_id = dex_file->GetMethodId(method_idx); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ObjPtr klass = class_linker->LookupResolvedType( method_id.class_idx_, dex_cache, /* class_loader= */ nullptr); if (klass == nullptr) { return; } // Call FindResolvedMethod to populate the dex cache. class_linker->FindResolvedMethod(klass, dex_cache, /* class_loader= */ nullptr, method_idx); } struct DexCacheStats { uint32_t num_strings; uint32_t num_types; uint32_t num_fields; uint32_t num_methods; DexCacheStats() : num_strings(0), num_types(0), num_fields(0), num_methods(0) {} }; static const bool kPreloadDexCachesEnabled = true; // Disabled because it takes a long time (extra half second) but // gives almost no benefit in terms of saving private dirty pages. static const bool kPreloadDexCachesStrings = false; static const bool kPreloadDexCachesTypes = true; static const bool kPreloadDexCachesFieldsAndMethods = true; static const bool kPreloadDexCachesCollectStats = true; static void PreloadDexCachesStatsTotal(DexCacheStats* total) { if (!kPreloadDexCachesCollectStats) { return; } ClassLinker* linker = Runtime::Current()->GetClassLinker(); const std::vector& boot_class_path = linker->GetBootClassPath(); for (size_t i = 0; i< boot_class_path.size(); i++) { const DexFile* dex_file = boot_class_path[i]; CHECK(dex_file != nullptr); total->num_strings += dex_file->NumStringIds(); total->num_fields += dex_file->NumFieldIds(); total->num_methods += dex_file->NumMethodIds(); total->num_types += dex_file->NumTypeIds(); } } static void PreloadDexCachesStatsFilled(DexCacheStats* filled) REQUIRES_SHARED(Locks::mutator_lock_) { if (!kPreloadDexCachesCollectStats) { return; } // TODO: Update for hash-based DexCache arrays. ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Thread* const self = Thread::Current(); for (const DexFile* dex_file : class_linker->GetBootClassPath()) { CHECK(dex_file != nullptr); // In fallback mode, not all boot classpath components might be registered, yet. if (!class_linker->IsDexFileRegistered(self, *dex_file)) { continue; } const ObjPtr dex_cache = class_linker->FindDexCache(self, *dex_file); DCHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded. for (size_t j = 0, num_strings = dex_cache->NumStrings(); j < num_strings; ++j) { auto pair = dex_cache->GetStrings()[j].load(std::memory_order_relaxed); if (!pair.object.IsNull()) { filled->num_strings++; } } for (size_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; ++j) { auto pair = dex_cache->GetResolvedTypes()[j].load(std::memory_order_relaxed); if (!pair.object.IsNull()) { filled->num_types++; } } for (size_t j = 0, num_fields = dex_cache->NumResolvedFields(); j < num_fields; ++j) { auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedFields(), j, kRuntimePointerSize); if (pair.object != nullptr) { filled->num_fields++; } } for (size_t j = 0, num_methods = dex_cache->NumResolvedMethods(); j < num_methods; ++j) { auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedMethods(), j, kRuntimePointerSize); if (pair.object != nullptr) { filled->num_methods++; } } } } // TODO: http://b/11309598 This code was ported over based on the // Dalvik version. However, ART has similar code in other places such // as the CompilerDriver. This code could probably be refactored to // serve both uses. static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { if (!kPreloadDexCachesEnabled) { return; } ScopedObjectAccess soa(env); DexCacheStats total; DexCacheStats before; if (kPreloadDexCachesCollectStats) { LOG(INFO) << "VMRuntime.preloadDexCaches starting"; PreloadDexCachesStatsTotal(&total); PreloadDexCachesStatsFilled(&before); } Runtime* runtime = Runtime::Current(); ClassLinker* linker = runtime->GetClassLinker(); // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings StringTable strings; if (kPreloadDexCachesStrings) { PreloadDexCachesStringsVisitor visitor(&strings); runtime->GetInternTable()->VisitRoots(&visitor, kVisitRootFlagAllRoots); } const std::vector& boot_class_path = linker->GetBootClassPath(); for (size_t i = 0; i < boot_class_path.size(); i++) { const DexFile* dex_file = boot_class_path[i]; CHECK(dex_file != nullptr); ObjPtr dex_cache = linker->RegisterDexFile(*dex_file, nullptr); CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded. if (kPreloadDexCachesStrings) { for (size_t j = 0; j < dex_cache->NumStrings(); j++) { PreloadDexCachesResolveString(dex_cache, dex::StringIndex(j), strings); } } if (kPreloadDexCachesTypes) { for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) { PreloadDexCachesResolveType(soa.Self(), dex_cache, dex::TypeIndex(j)); } } if (kPreloadDexCachesFieldsAndMethods) { for (ClassAccessor accessor : dex_file->GetClasses()) { for (const ClassAccessor::Field& field : accessor.GetFields()) { PreloadDexCachesResolveField(dex_cache, field.GetIndex(), field.IsStatic()); } for (const ClassAccessor::Method& method : accessor.GetMethods()) { PreloadDexCachesResolveMethod(dex_cache, method.GetIndex()); } } } } if (kPreloadDexCachesCollectStats) { DexCacheStats after; PreloadDexCachesStatsFilled(&after); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d", total.num_strings, before.num_strings, after.num_strings); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches types total=%d before=%d after=%d", total.num_types, before.num_types, after.num_types); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d", total.num_fields, before.num_fields, after.num_fields); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d", total.num_methods, before.num_methods, after.num_methods); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches finished"); } } /* * This is called by the framework when it knows the application directory and * process name. */ static void VMRuntime_registerAppInfo(JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED, jstring profile_file, jobjectArray code_paths) { std::vector code_paths_vec; int code_paths_length = env->GetArrayLength(code_paths); for (int i = 0; i < code_paths_length; i++) { jstring code_path = reinterpret_cast(env->GetObjectArrayElement(code_paths, i)); const char* raw_code_path = env->GetStringUTFChars(code_path, nullptr); code_paths_vec.push_back(raw_code_path); env->ReleaseStringUTFChars(code_path, raw_code_path); } const char* raw_profile_file = env->GetStringUTFChars(profile_file, nullptr); std::string profile_file_str(raw_profile_file); env->ReleaseStringUTFChars(profile_file, raw_profile_file); Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str); } static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) { ScopedUtfChars instruction_set(env, java_instruction_set); if (instruction_set.c_str() == nullptr) { return JNI_FALSE; } InstructionSet isa = GetInstructionSetFromString(instruction_set.c_str()); if (isa == InstructionSet::kNone) { ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str())); env->ThrowNew(iae.get(), message.c_str()); return JNI_FALSE; } return gc::space::ImageSpace::IsBootClassPathOnDisk(isa); } static jstring VMRuntime_getCurrentInstructionSet(JNIEnv* env, jclass) { return env->NewStringUTF(GetInstructionSetString(kRuntimeISA)); } static jboolean VMRuntime_didPruneDalvikCache(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) { return Runtime::Current()->GetPrunedDalvikCache() ? JNI_TRUE : JNI_FALSE; } static void VMRuntime_setSystemDaemonThreadPriority(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) { #ifdef ART_TARGET_ANDROID Thread* self = Thread::Current(); DCHECK(self != nullptr); pid_t tid = self->GetTid(); // We use a priority lower than the default for the system daemon threads (eg HeapTaskDaemon) to // avoid jank due to CPU contentions between GC and other UI-related threads. b/36631902. // We may use a native priority that doesn't have a corresponding java.lang.Thread-level priority. static constexpr int kSystemDaemonNiceValue = 4; // priority 124 if (setpriority(PRIO_PROCESS, tid, kSystemDaemonNiceValue) != 0) { PLOG(INFO) << *self << " setpriority(PRIO_PROCESS, " << tid << ", " << kSystemDaemonNiceValue << ") failed"; } #endif } static void VMRuntime_setDedupeHiddenApiWarnings(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean dedupe) { Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe); } static void VMRuntime_setProcessPackageName(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jstring java_package_name) { ScopedUtfChars package_name(env, java_package_name); Runtime::Current()->SetProcessPackageName(package_name.c_str()); } static void VMRuntime_setProcessDataDirectory(JNIEnv* env, jclass, jstring java_data_dir) { ScopedUtfChars data_dir(env, java_data_dir); Runtime::Current()->SetProcessDataDirectory(data_dir.c_str()); } static void VMRuntime_bootCompleted(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) { jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { jit->BootCompleted(); } } class ClearJitCountersVisitor : public ClassVisitor { public: bool operator()(ObjPtr klass) override REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid some types of classes that don't need their methods visited. if (klass->IsProxyClass() || klass->IsArrayClass() || klass->IsPrimitive() || !klass->IsResolved() || klass->IsErroneousResolved()) { return true; } for (ArtMethod& m : klass->GetMethods(kRuntimePointerSize)) { if (!m.IsAbstract()) { if (m.GetCounter() != 0) { m.SetCounter(0); } } } return true; } }; static void VMRuntime_resetJitCounters(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { ScopedObjectAccess soa(env); ClearJitCountersVisitor visitor; Runtime::Current()->GetClassLinker()->VisitClasses(&visitor); } static jboolean VMRuntime_isValidClassLoaderContext(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jstring jencoded_class_loader_context) { if (UNLIKELY(jencoded_class_loader_context == nullptr)) { ScopedFastNativeObjectAccess soa(env); ThrowNullPointerException("encoded_class_loader_context == null"); return false; } ScopedUtfChars encoded_class_loader_context(env, jencoded_class_loader_context); return ClassLoaderContext::IsValidEncoding(encoded_class_loader_context.c_str()); } static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, clampGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, setHiddenApiAccessLogSamplingRate, "(I)V"), NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), NATIVE_METHOD(VMRuntime, isJavaDebuggable, "()Z"), NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), FAST_NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), FAST_NATIVE_METHOD(VMRuntime, newUnpaddedArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"), NATIVE_METHOD(VMRuntime, setDisabledCompatChangesNative, "([J)V"), NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(J)V"), NATIVE_METHOD(VMRuntime, registerNativeFree, "(J)V"), NATIVE_METHOD(VMRuntime, getNotifyNativeInterval, "()I"), NATIVE_METHOD(VMRuntime, getFinalizerTimeoutMs, "()J"), NATIVE_METHOD(VMRuntime, notifyNativeAllocationsInternal, "()V"), NATIVE_METHOD(VMRuntime, notifyStartupCompleted, "()V"), NATIVE_METHOD(VMRuntime, registerSensitiveThread, "()V"), NATIVE_METHOD(VMRuntime, requestConcurrentGC, "()V"), NATIVE_METHOD(VMRuntime, requestHeapTrim, "()V"), NATIVE_METHOD(VMRuntime, runHeapTasks, "()V"), NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"), NATIVE_METHOD(VMRuntime, startHeapTaskProcessor, "()V"), NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, stopHeapTaskProcessor, "()V"), NATIVE_METHOD(VMRuntime, trimHeap, "()V"), NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, vmInstructionSet, "()Ljava/lang/String;"), FAST_NATIVE_METHOD(VMRuntime, is64Bit, "()Z"), FAST_NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "()Z"), NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;[Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"), NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"), NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"), NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"), NATIVE_METHOD(VMRuntime, setProcessPackageName, "(Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, setProcessDataDirectory, "(Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, bootCompleted, "()V"), NATIVE_METHOD(VMRuntime, resetJitCounters, "()V"), NATIVE_METHOD(VMRuntime, isValidClassLoaderContext, "(Ljava/lang/String;)Z"), }; void register_dalvik_system_VMRuntime(JNIEnv* env) { REGISTER_NATIVE_METHODS("dalvik/system/VMRuntime"); } } // namespace art