/*
* 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.
*/
/*
* dalvik.system.VMRuntime
*/
#include "Dalvik.h"
#include "ScopedPthreadMutexLock.h"
#include "UniquePtr.h"
#include "alloc/HeapSource.h"
#include "alloc/Visit.h"
#include "libdex/DexClass.h"
#include "native/InternalNativePriv.h"
#include <limits.h>
#include <map>
/*
* public native float getTargetHeapUtilization()
*
* Gets the current ideal heap utilization, represented as a number
* between zero and one.
*/
static void Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization(
const u4* args, JValue* pResult)
{
UNUSED_PARAMETER(args);
RETURN_FLOAT(dvmGetTargetHeapUtilization());
}
/*
* native float nativeSetTargetHeapUtilization()
*
* Sets the current ideal heap utilization, represented as a number
* between zero and one. Returns the old utilization.
*
* Note that this is NOT static.
*/
static void Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization(
const u4* args, JValue* pResult)
{
dvmSetTargetHeapUtilization(dvmU4ToFloat(args[1]));
RETURN_VOID();
}
/*
* public native void startJitCompilation()
*
* Callback function from the framework to indicate that an app has gone
* through the startup phase and it is time to enable the JIT compiler.
*/
static void Dalvik_dalvik_system_VMRuntime_startJitCompilation(const u4* args,
JValue* pResult)
{
#if defined(WITH_JIT)
if (gDvm.executionMode == kExecutionModeJit && gDvmJit.disableJit == false) {
ScopedPthreadMutexLock lock(&gDvmJit.compilerLock);
gDvmJit.alreadyEnabledViaFramework = true;
pthread_cond_signal(&gDvmJit.compilerQueueActivity);
}
#endif
RETURN_VOID();
}
/*
* public native void disableJitCompilation()
*
* Callback function from the framework to indicate that a VM instance wants to
* permanently disable the JIT compiler. Currently only the system server uses
* this interface when it detects system-wide safe mode is enabled.
*/
static void Dalvik_dalvik_system_VMRuntime_disableJitCompilation(const u4* args,
JValue* pResult)
{
#if defined(WITH_JIT)
if (gDvm.executionMode == kExecutionModeJit) {
gDvmJit.disableJit = true;
}
#endif
RETURN_VOID();
}
static void Dalvik_dalvik_system_VMRuntime_newNonMovableArray(const u4* args,
JValue* pResult)
{
ClassObject* elementClass = (ClassObject*) args[1];
int length = args[2];
if (elementClass == NULL) {
dvmThrowNullPointerException("elementClass == null");
RETURN_VOID();
}
if (length < 0) {
dvmThrowNegativeArraySizeException(length);
RETURN_VOID();
}
// TODO: right now, we don't have a copying collector, so there's no need
// to do anything special here, but we ought to pass the non-movability
// through to the allocator.
ClassObject* arrayClass = dvmFindArrayClassForElement(elementClass);
ArrayObject* newArray = dvmAllocArrayByClass(arrayClass,
length,
ALLOC_NON_MOVING);
if (newArray == NULL) {
assert(dvmCheckException(dvmThreadSelf()));
RETURN_VOID();
}
dvmReleaseTrackedAlloc((Object*) newArray, NULL);
RETURN_PTR(newArray);
}
static void Dalvik_dalvik_system_VMRuntime_addressOf(const u4* args,
JValue* pResult)
{
ArrayObject* array = (ArrayObject*) args[1];
if (!dvmIsArray(array)) {
dvmThrowIllegalArgumentException(NULL);
RETURN_VOID();
}
// TODO: we should also check that this is a non-movable array.
s8 result = (uintptr_t) array->contents;
RETURN_LONG(result);
}
static void Dalvik_dalvik_system_VMRuntime_clearGrowthLimit(const u4* args,
JValue* pResult)
{
dvmClearGrowthLimit();
RETURN_VOID();
}
static void Dalvik_dalvik_system_VMRuntime_isDebuggerActive(
const u4* args, JValue* pResult)
{
RETURN_BOOLEAN(gDvm.debuggerActive || gDvm.nativeDebuggerActive);
}
static void Dalvik_dalvik_system_VMRuntime_properties(const u4* args,
JValue* pResult)
{
ArrayObject* result = dvmCreateStringArray(*gDvm.properties);
dvmReleaseTrackedAlloc((Object*) result, dvmThreadSelf());
RETURN_PTR(result);
}
static void returnCString(JValue* pResult, const char* s)
{
Object* result = (Object*) dvmCreateStringFromCstr(s);
dvmReleaseTrackedAlloc(result, dvmThreadSelf());
RETURN_PTR(result);
}
static void Dalvik_dalvik_system_VMRuntime_bootClassPath(const u4* args,
JValue* pResult)
{
returnCString(pResult, gDvm.bootClassPathStr);
}
static void Dalvik_dalvik_system_VMRuntime_classPath(const u4* args,
JValue* pResult)
{
returnCString(pResult, gDvm.classPathStr);
}
static void Dalvik_dalvik_system_VMRuntime_vmVersion(const u4* args,
JValue* pResult)
{
char buf[64];
sprintf(buf, "%d.%d.%d",
DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
returnCString(pResult, buf);
}
static void Dalvik_dalvik_system_VMRuntime_vmLibrary(const u4* args,
JValue* pResult)
{
returnCString(pResult, "libdvm.so");
}
static void Dalvik_dalvik_system_VMRuntime_setTargetSdkVersion(const u4* args,
JValue* pResult)
{
// This is the target SDK version of the app we're about to run.
// Note that this value may be CUR_DEVELOPMENT (10000).
// Note that this value may be 0, meaning "current".
int targetSdkVersion = args[1];
if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) {
if (gDvmJni.useCheckJni) {
ALOGI("CheckJNI enabled: not enabling JNI app bug workarounds.");
} else {
ALOGI("Enabling JNI app bug workarounds for target SDK version %i...",
targetSdkVersion);
gDvmJni.workAroundAppJniBugs = true;
}
}
RETURN_VOID();
}
static void Dalvik_dalvik_system_VMRuntime_registerNativeAllocation(const u4* args,
JValue* pResult)
{
int bytes = args[1];
if (bytes < 0) {
dvmThrowRuntimeException("allocation size negative");
} else {
dvmHeapSourceRegisterNativeAllocation(bytes);
}
RETURN_VOID();
}
static void Dalvik_dalvik_system_VMRuntime_registerNativeFree(const u4* args,
JValue* pResult)
{
int bytes = args[1];
if (bytes < 0) {
dvmThrowRuntimeException("allocation size negative");
} else {
dvmHeapSourceRegisterNativeFree(bytes);
}
RETURN_VOID();
}
static DvmDex* getDvmDexFromClassPathEntry(ClassPathEntry* cpe) {
if (cpe->kind == kCpeDex) {
return ((RawDexFile*) cpe->ptr)->pDvmDex;
}
if (cpe->kind == kCpeJar) {
return ((JarFile*) cpe->ptr)->pDvmDex;
}
LOG_ALWAYS_FATAL("Unknown cpe->kind=%d", cpe->kind);
}
typedef std::map<std::string, StringObject*> StringTable;
static void preloadDexCachesStringsVisitor(void* addr, u4 threadId, RootType type, void* arg) {
StringTable& table = *(StringTable*) arg;
StringObject* strObj = *(StringObject**) addr;
LOG_FATAL_IF(strObj->clazz != gDvm.classJavaLangString, "Unknown class for supposed string");
char* newStr = dvmCreateCstrFromString(strObj);
// ALOGI("VMRuntime.preloadDexCaches interned=%s", newStr);
table[newStr] = strObj;
free(newStr);
}
// Based on dvmResolveString.
static void preloadDexCachesResolveString(DvmDex* pDvmDex,
uint32_t stringIdx,
StringTable& strings) {
StringObject* string = dvmDexGetResolvedString(pDvmDex, stringIdx);
if (string != NULL) {
return;
}
const DexFile* pDexFile = pDvmDex->pDexFile;
uint32_t utf16Size;
const char* utf8 = dexStringAndSizeById(pDexFile, stringIdx, &utf16Size);
string = strings[utf8];
if (string == NULL) {
return;
}
// ALOGI("VMRuntime.preloadDexCaches found string=%s", utf8);
dvmDexSetResolvedString(pDvmDex, stringIdx, string);
}
// Based on dvmResolveClass.
static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) {
ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx);
if (clazz != NULL) {
return;
}
const DexFile* pDexFile = pDvmDex->pDexFile;
const char* className = dexStringByTypeIdx(pDexFile, typeIdx);
if (className[0] != '\0' && className[1] == '\0') {
/* primitive type */
clazz = dvmFindPrimitiveClass(className[0]);
} else {
clazz = dvmLookupClass(className, NULL, true);
}
if (clazz == NULL) {
return;
}
// Skip uninitialized classes because filled cache entry implies it is initialized.
if (!dvmIsClassInitialized(clazz)) {
// ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className);
return;
}
// ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className);
dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz);
}
// Based on dvmResolveInstField/dvmResolveStaticField.
static void preloadDexCachesResolveField(DvmDex* pDvmDex, uint32_t fieldIdx, bool instance) {
Field* field = dvmDexGetResolvedField(pDvmDex, fieldIdx);
if (field != NULL) {
return;
}
const DexFile* pDexFile = pDvmDex->pDexFile;
const DexFieldId* pFieldId = dexGetFieldId(pDexFile, fieldIdx);
ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pFieldId->classIdx);
if (clazz == NULL) {
return;
}
// Skip static fields for uninitialized classes because a filled
// cache entry implies the class is initialized.
if (!instance && !dvmIsClassInitialized(clazz)) {
return;
}
const char* fieldName = dexStringById(pDexFile, pFieldId->nameIdx);
const char* signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
if (instance) {
field = dvmFindInstanceFieldHier(clazz, fieldName, signature);
} else {
field = dvmFindStaticFieldHier(clazz, fieldName, signature);
}
if (field == NULL) {
return;
}
// ALOGI("VMRuntime.preloadDexCaches found field %s %s.%s",
// signature, clazz->descriptor, fieldName);
dvmDexSetResolvedField(pDvmDex, fieldIdx, field);
}
// Based on dvmResolveMethod.
static void preloadDexCachesResolveMethod(DvmDex* pDvmDex,
uint32_t methodIdx,
MethodType methodType) {
Method* method = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
if (method != NULL) {
return;
}
const DexFile* pDexFile = pDvmDex->pDexFile;
const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pMethodId->classIdx);
if (clazz == NULL) {
return;
}
// Skip static methods for uninitialized classes because a filled
// cache entry implies the class is initialized.
if ((methodType == METHOD_STATIC) && !dvmIsClassInitialized(clazz)) {
return;
}
const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx);
DexProto proto;
dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
if (methodType == METHOD_DIRECT) {
method = dvmFindDirectMethod(clazz, methodName, &proto);
} else if (methodType == METHOD_STATIC) {
method = dvmFindDirectMethodHier(clazz, methodName, &proto);
} else {
method = dvmFindVirtualMethodHier(clazz, methodName, &proto);
}
if (method == NULL) {
return;
}
// ALOGI("VMRuntime.preloadDexCaches found method %s.%s",
// clazz->descriptor, methodName);
dvmDexSetResolvedMethod(pDvmDex, methodIdx, method);
}
struct DexCacheStats {
uint32_t numStrings;
uint32_t numTypes;
uint32_t numFields;
uint32_t numMethods;
DexCacheStats() : numStrings(0), numTypes(0), numFields(0), numMethods(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 = false;
static void preloadDexCachesStatsTotal(DexCacheStats* total) {
if (!kPreloadDexCachesCollectStats) {
return;
}
for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
const DexHeader* pHeader = pDvmDex->pHeader;
total->numStrings += pHeader->stringIdsSize;
total->numFields += pHeader->fieldIdsSize;
total->numMethods += pHeader->methodIdsSize;
total->numTypes += pHeader->typeIdsSize;
}
}
static void preloadDexCachesStatsFilled(DexCacheStats* filled) {
if (!kPreloadDexCachesCollectStats) {
return;
}
for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
const DexHeader* pHeader = pDvmDex->pHeader;
for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
StringObject* string = dvmDexGetResolvedString(pDvmDex, i);
if (string != NULL) {
filled->numStrings++;
}
}
for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, i);
if (clazz != NULL) {
filled->numTypes++;
}
}
for (size_t i = 0; i < pHeader->fieldIdsSize; i++) {
Field* field = dvmDexGetResolvedField(pDvmDex, i);
if (field != NULL) {
filled->numFields++;
}
}
for (size_t i = 0; i < pHeader->methodIdsSize; i++) {
Method* method = dvmDexGetResolvedMethod(pDvmDex, i);
if (method != NULL) {
filled->numMethods++;
}
}
}
}
static void Dalvik_dalvik_system_VMRuntime_preloadDexCaches(const u4* args, JValue* pResult)
{
if (!kPreloadDexCachesEnabled) {
return;
}
DexCacheStats total;
DexCacheStats before;
if (kPreloadDexCachesCollectStats) {
ALOGI("VMRuntime.preloadDexCaches starting");
preloadDexCachesStatsTotal(&total);
preloadDexCachesStatsFilled(&before);
}
// We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
StringTable strings;
if (kPreloadDexCachesStrings) {
dvmLockMutex(&gDvm.internLock);
dvmHashTableLock(gDvm.literalStrings);
for (int i = 0; i < gDvm.literalStrings->tableSize; ++i) {
HashEntry *entry = &gDvm.literalStrings->pEntries[i];
if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
preloadDexCachesStringsVisitor(&entry->data, 0, ROOT_INTERNED_STRING, &strings);
}
}
dvmHashTableUnlock(gDvm.literalStrings);
dvmUnlockMutex(&gDvm.internLock);
}
for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
const DexHeader* pHeader = pDvmDex->pHeader;
const DexFile* pDexFile = pDvmDex->pDexFile;
if (kPreloadDexCachesStrings) {
for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
preloadDexCachesResolveString(pDvmDex, i, strings);
}
}
if (kPreloadDexCachesTypes) {
for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
preloadDexCachesResolveType(pDvmDex, i);
}
}
if (kPreloadDexCachesFieldsAndMethods) {
for (size_t classDefIndex = 0;
classDefIndex < pHeader->classDefsSize;
classDefIndex++) {
const DexClassDef* pClassDef = dexGetClassDef(pDexFile, classDefIndex);
const u1* pEncodedData = dexGetClassData(pDexFile, pClassDef);
UniquePtr<DexClassData> pClassData(dexReadAndVerifyClassData(&pEncodedData, NULL));
if (pClassData.get() == NULL) {
continue;
}
for (uint32_t fieldIndex = 0;
fieldIndex < pClassData->header.staticFieldsSize;
fieldIndex++) {
const DexField* pField = &pClassData->staticFields[fieldIndex];
preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, false);
}
for (uint32_t fieldIndex = 0;
fieldIndex < pClassData->header.instanceFieldsSize;
fieldIndex++) {
const DexField* pField = &pClassData->instanceFields[fieldIndex];
preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, true);
}
for (uint32_t methodIndex = 0;
methodIndex < pClassData->header.directMethodsSize;
methodIndex++) {
const DexMethod* pDexMethod = &pClassData->directMethods[methodIndex];
MethodType methodType = (((pDexMethod->accessFlags & ACC_STATIC) != 0) ?
METHOD_STATIC :
METHOD_DIRECT);
preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, methodType);
}
for (uint32_t methodIndex = 0;
methodIndex < pClassData->header.virtualMethodsSize;
methodIndex++) {
const DexMethod* pDexMethod = &pClassData->virtualMethods[methodIndex];
preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, METHOD_VIRTUAL);
}
}
}
}
if (kPreloadDexCachesCollectStats) {
DexCacheStats after;
preloadDexCachesStatsFilled(&after);
ALOGI("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d",
total.numStrings, before.numStrings, after.numStrings);
ALOGI("VMRuntime.preloadDexCaches types total=%d before=%d after=%d",
total.numTypes, before.numTypes, after.numTypes);
ALOGI("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d",
total.numFields, before.numFields, after.numFields);
ALOGI("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d",
total.numMethods, before.numMethods, after.numMethods);
ALOGI("VMRuntime.preloadDexCaches finished");
}
RETURN_VOID();
}
const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
{ "addressOf", "(Ljava/lang/Object;)J",
Dalvik_dalvik_system_VMRuntime_addressOf },
{ "bootClassPath", "()Ljava/lang/String;",
Dalvik_dalvik_system_VMRuntime_bootClassPath },
{ "classPath", "()Ljava/lang/String;",
Dalvik_dalvik_system_VMRuntime_classPath },
{ "clearGrowthLimit", "()V",
Dalvik_dalvik_system_VMRuntime_clearGrowthLimit },
{ "disableJitCompilation", "()V",
Dalvik_dalvik_system_VMRuntime_disableJitCompilation },
{ "isDebuggerActive", "()Z",
Dalvik_dalvik_system_VMRuntime_isDebuggerActive },
{ "getTargetHeapUtilization", "()F",
Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
{ "nativeSetTargetHeapUtilization", "(F)V",
Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization },
{ "newNonMovableArray", "(Ljava/lang/Class;I)Ljava/lang/Object;",
Dalvik_dalvik_system_VMRuntime_newNonMovableArray },
{ "properties", "()[Ljava/lang/String;",
Dalvik_dalvik_system_VMRuntime_properties },
{ "setTargetSdkVersion", "(I)V",
Dalvik_dalvik_system_VMRuntime_setTargetSdkVersion },
{ "startJitCompilation", "()V",
Dalvik_dalvik_system_VMRuntime_startJitCompilation },
{ "vmVersion", "()Ljava/lang/String;",
Dalvik_dalvik_system_VMRuntime_vmVersion },
{ "vmLibrary", "()Ljava/lang/String;",
Dalvik_dalvik_system_VMRuntime_vmLibrary },
{ "registerNativeAllocation", "(I)V",
Dalvik_dalvik_system_VMRuntime_registerNativeAllocation },
{ "registerNativeFree", "(I)V",
Dalvik_dalvik_system_VMRuntime_registerNativeFree },
{ "preloadDexCaches", "()V",
Dalvik_dalvik_system_VMRuntime_preloadDexCaches },
{ NULL, NULL, NULL },
};