1/* 2 * Copyright (C) 2008 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/* 18 * dalvik.system.VMRuntime 19 */ 20#include "Dalvik.h" 21#include "ScopedPthreadMutexLock.h" 22#include "UniquePtr.h" 23#include "alloc/HeapSource.h" 24#include "alloc/Visit.h" 25#include "libdex/DexClass.h" 26#include "native/InternalNativePriv.h" 27 28#include <limits.h> 29 30#include <map> 31 32/* 33 * public native float getTargetHeapUtilization() 34 * 35 * Gets the current ideal heap utilization, represented as a number 36 * between zero and one. 37 */ 38static void Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization( 39 const u4* args, JValue* pResult) 40{ 41 UNUSED_PARAMETER(args); 42 43 RETURN_FLOAT(dvmGetTargetHeapUtilization()); 44} 45 46/* 47 * native float nativeSetTargetHeapUtilization() 48 * 49 * Sets the current ideal heap utilization, represented as a number 50 * between zero and one. Returns the old utilization. 51 * 52 * Note that this is NOT static. 53 */ 54static void Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization( 55 const u4* args, JValue* pResult) 56{ 57 dvmSetTargetHeapUtilization(dvmU4ToFloat(args[1])); 58 59 RETURN_VOID(); 60} 61 62/* 63 * public native void startJitCompilation() 64 * 65 * Callback function from the framework to indicate that an app has gone 66 * through the startup phase and it is time to enable the JIT compiler. 67 */ 68static void Dalvik_dalvik_system_VMRuntime_startJitCompilation(const u4* args, 69 JValue* pResult) 70{ 71#if defined(WITH_JIT) 72 if (gDvm.executionMode == kExecutionModeJit && gDvmJit.disableJit == false) { 73 ScopedPthreadMutexLock lock(&gDvmJit.compilerLock); 74 gDvmJit.alreadyEnabledViaFramework = true; 75 pthread_cond_signal(&gDvmJit.compilerQueueActivity); 76 } 77#endif 78 RETURN_VOID(); 79} 80 81/* 82 * public native void disableJitCompilation() 83 * 84 * Callback function from the framework to indicate that a VM instance wants to 85 * permanently disable the JIT compiler. Currently only the system server uses 86 * this interface when it detects system-wide safe mode is enabled. 87 */ 88static void Dalvik_dalvik_system_VMRuntime_disableJitCompilation(const u4* args, 89 JValue* pResult) 90{ 91#if defined(WITH_JIT) 92 if (gDvm.executionMode == kExecutionModeJit) { 93 gDvmJit.disableJit = true; 94 } 95#endif 96 RETURN_VOID(); 97} 98 99static void Dalvik_dalvik_system_VMRuntime_newNonMovableArray(const u4* args, 100 JValue* pResult) 101{ 102 ClassObject* elementClass = (ClassObject*) args[1]; 103 int length = args[2]; 104 105 if (elementClass == NULL) { 106 dvmThrowNullPointerException("elementClass == null"); 107 RETURN_VOID(); 108 } 109 if (length < 0) { 110 dvmThrowNegativeArraySizeException(length); 111 RETURN_VOID(); 112 } 113 114 // TODO: right now, we don't have a copying collector, so there's no need 115 // to do anything special here, but we ought to pass the non-movability 116 // through to the allocator. 117 ClassObject* arrayClass = dvmFindArrayClassForElement(elementClass); 118 ArrayObject* newArray = dvmAllocArrayByClass(arrayClass, 119 length, 120 ALLOC_NON_MOVING); 121 if (newArray == NULL) { 122 assert(dvmCheckException(dvmThreadSelf())); 123 RETURN_VOID(); 124 } 125 dvmReleaseTrackedAlloc((Object*) newArray, NULL); 126 127 RETURN_PTR(newArray); 128} 129 130static void Dalvik_dalvik_system_VMRuntime_addressOf(const u4* args, 131 JValue* pResult) 132{ 133 ArrayObject* array = (ArrayObject*) args[1]; 134 if (!dvmIsArray(array)) { 135 dvmThrowIllegalArgumentException(NULL); 136 RETURN_VOID(); 137 } 138 // TODO: we should also check that this is a non-movable array. 139 s8 result = (uintptr_t) array->contents; 140 RETURN_LONG(result); 141} 142 143static void Dalvik_dalvik_system_VMRuntime_clearGrowthLimit(const u4* args, 144 JValue* pResult) 145{ 146 dvmClearGrowthLimit(); 147 RETURN_VOID(); 148} 149 150static void Dalvik_dalvik_system_VMRuntime_isDebuggerActive( 151 const u4* args, JValue* pResult) 152{ 153 RETURN_BOOLEAN(gDvm.debuggerActive || gDvm.nativeDebuggerActive); 154} 155 156static void Dalvik_dalvik_system_VMRuntime_properties(const u4* args, 157 JValue* pResult) 158{ 159 ArrayObject* result = dvmCreateStringArray(*gDvm.properties); 160 dvmReleaseTrackedAlloc((Object*) result, dvmThreadSelf()); 161 RETURN_PTR(result); 162} 163 164static void returnCString(JValue* pResult, const char* s) 165{ 166 Object* result = (Object*) dvmCreateStringFromCstr(s); 167 dvmReleaseTrackedAlloc(result, dvmThreadSelf()); 168 RETURN_PTR(result); 169} 170 171static void Dalvik_dalvik_system_VMRuntime_bootClassPath(const u4* args, 172 JValue* pResult) 173{ 174 returnCString(pResult, gDvm.bootClassPathStr); 175} 176 177static void Dalvik_dalvik_system_VMRuntime_classPath(const u4* args, 178 JValue* pResult) 179{ 180 returnCString(pResult, gDvm.classPathStr); 181} 182 183static void Dalvik_dalvik_system_VMRuntime_vmVersion(const u4* args, 184 JValue* pResult) 185{ 186 char buf[64]; 187 sprintf(buf, "%d.%d.%d", 188 DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION); 189 returnCString(pResult, buf); 190} 191 192static void Dalvik_dalvik_system_VMRuntime_vmLibrary(const u4* args, 193 JValue* pResult) 194{ 195 returnCString(pResult, "libdvm.so"); 196} 197 198static void Dalvik_dalvik_system_VMRuntime_setTargetSdkVersion(const u4* args, 199 JValue* pResult) 200{ 201 // This is the target SDK version of the app we're about to run. 202 // Note that this value may be CUR_DEVELOPMENT (10000). 203 // Note that this value may be 0, meaning "current". 204 int targetSdkVersion = args[1]; 205 if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) { 206 if (gDvmJni.useCheckJni) { 207 ALOGI("CheckJNI enabled: not enabling JNI app bug workarounds."); 208 } else { 209 ALOGI("Enabling JNI app bug workarounds for target SDK version %i...", 210 targetSdkVersion); 211 gDvmJni.workAroundAppJniBugs = true; 212 } 213 } 214 RETURN_VOID(); 215} 216 217static void Dalvik_dalvik_system_VMRuntime_registerNativeAllocation(const u4* args, 218 JValue* pResult) 219{ 220 int bytes = args[1]; 221 if (bytes < 0) { 222 dvmThrowRuntimeException("allocation size negative"); 223 } else { 224 dvmHeapSourceRegisterNativeAllocation(bytes); 225 } 226 RETURN_VOID(); 227} 228 229static void Dalvik_dalvik_system_VMRuntime_registerNativeFree(const u4* args, 230 JValue* pResult) 231{ 232 int bytes = args[1]; 233 if (bytes < 0) { 234 dvmThrowRuntimeException("allocation size negative"); 235 } else { 236 dvmHeapSourceRegisterNativeFree(bytes); 237 } 238 RETURN_VOID(); 239} 240 241static DvmDex* getDvmDexFromClassPathEntry(ClassPathEntry* cpe) { 242 if (cpe->kind == kCpeDex) { 243 return ((RawDexFile*) cpe->ptr)->pDvmDex; 244 } 245 if (cpe->kind == kCpeJar) { 246 return ((JarFile*) cpe->ptr)->pDvmDex; 247 } 248 LOG_ALWAYS_FATAL("Unknown cpe->kind=%d", cpe->kind); 249} 250 251typedef std::map<std::string, StringObject*> StringTable; 252 253static void preloadDexCachesStringsVisitor(void* addr, u4 threadId, RootType type, void* arg) { 254 StringTable& table = *(StringTable*) arg; 255 StringObject* strObj = *(StringObject**) addr; 256 LOG_FATAL_IF(strObj->clazz != gDvm.classJavaLangString, "Unknown class for supposed string"); 257 char* newStr = dvmCreateCstrFromString(strObj); 258 // ALOGI("VMRuntime.preloadDexCaches interned=%s", newStr); 259 table[newStr] = strObj; 260 free(newStr); 261} 262 263// Based on dvmResolveString. 264static void preloadDexCachesResolveString(DvmDex* pDvmDex, 265 uint32_t stringIdx, 266 StringTable& strings) { 267 StringObject* string = dvmDexGetResolvedString(pDvmDex, stringIdx); 268 if (string != NULL) { 269 return; 270 } 271 const DexFile* pDexFile = pDvmDex->pDexFile; 272 uint32_t utf16Size; 273 const char* utf8 = dexStringAndSizeById(pDexFile, stringIdx, &utf16Size); 274 string = strings[utf8]; 275 if (string == NULL) { 276 return; 277 } 278 // ALOGI("VMRuntime.preloadDexCaches found string=%s", utf8); 279 dvmDexSetResolvedString(pDvmDex, stringIdx, string); 280} 281 282// Based on dvmResolveClass. 283static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) { 284 ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx); 285 if (clazz != NULL) { 286 return; 287 } 288 const DexFile* pDexFile = pDvmDex->pDexFile; 289 const char* className = dexStringByTypeIdx(pDexFile, typeIdx); 290 if (className[0] != '\0' && className[1] == '\0') { 291 /* primitive type */ 292 clazz = dvmFindPrimitiveClass(className[0]); 293 } else { 294 clazz = dvmLookupClass(className, NULL, true); 295 } 296 if (clazz == NULL) { 297 return; 298 } 299 // Skip uninitialized classes because filled cache entry implies it is initialized. 300 if (!dvmIsClassInitialized(clazz)) { 301 // ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className); 302 return; 303 } 304 // ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className); 305 dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz); 306} 307 308// Based on dvmResolveInstField/dvmResolveStaticField. 309static void preloadDexCachesResolveField(DvmDex* pDvmDex, uint32_t fieldIdx, bool instance) { 310 Field* field = dvmDexGetResolvedField(pDvmDex, fieldIdx); 311 if (field != NULL) { 312 return; 313 } 314 const DexFile* pDexFile = pDvmDex->pDexFile; 315 const DexFieldId* pFieldId = dexGetFieldId(pDexFile, fieldIdx); 316 ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pFieldId->classIdx); 317 if (clazz == NULL) { 318 return; 319 } 320 // Skip static fields for uninitialized classes because a filled 321 // cache entry implies the class is initialized. 322 if (!instance && !dvmIsClassInitialized(clazz)) { 323 return; 324 } 325 const char* fieldName = dexStringById(pDexFile, pFieldId->nameIdx); 326 const char* signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx); 327 if (instance) { 328 field = dvmFindInstanceFieldHier(clazz, fieldName, signature); 329 } else { 330 field = dvmFindStaticFieldHier(clazz, fieldName, signature); 331 } 332 if (field == NULL) { 333 return; 334 } 335 // ALOGI("VMRuntime.preloadDexCaches found field %s %s.%s", 336 // signature, clazz->descriptor, fieldName); 337 dvmDexSetResolvedField(pDvmDex, fieldIdx, field); 338} 339 340// Based on dvmResolveMethod. 341static void preloadDexCachesResolveMethod(DvmDex* pDvmDex, 342 uint32_t methodIdx, 343 MethodType methodType) { 344 Method* method = dvmDexGetResolvedMethod(pDvmDex, methodIdx); 345 if (method != NULL) { 346 return; 347 } 348 const DexFile* pDexFile = pDvmDex->pDexFile; 349 const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx); 350 ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pMethodId->classIdx); 351 if (clazz == NULL) { 352 return; 353 } 354 // Skip static methods for uninitialized classes because a filled 355 // cache entry implies the class is initialized. 356 if ((methodType == METHOD_STATIC) && !dvmIsClassInitialized(clazz)) { 357 return; 358 } 359 const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx); 360 DexProto proto; 361 dexProtoSetFromMethodId(&proto, pDexFile, pMethodId); 362 363 if (methodType == METHOD_DIRECT) { 364 method = dvmFindDirectMethod(clazz, methodName, &proto); 365 } else if (methodType == METHOD_STATIC) { 366 method = dvmFindDirectMethodHier(clazz, methodName, &proto); 367 } else { 368 method = dvmFindVirtualMethodHier(clazz, methodName, &proto); 369 } 370 if (method == NULL) { 371 return; 372 } 373 // ALOGI("VMRuntime.preloadDexCaches found method %s.%s", 374 // clazz->descriptor, methodName); 375 dvmDexSetResolvedMethod(pDvmDex, methodIdx, method); 376} 377 378struct DexCacheStats { 379 uint32_t numStrings; 380 uint32_t numTypes; 381 uint32_t numFields; 382 uint32_t numMethods; 383 DexCacheStats() : numStrings(0), numTypes(0), numFields(0), numMethods(0) {}; 384}; 385 386static const bool kPreloadDexCachesEnabled = true; 387 388// Disabled because it takes a long time (extra half second) but 389// gives almost no benefit in terms of saving private dirty pages. 390static const bool kPreloadDexCachesStrings = false; 391 392static const bool kPreloadDexCachesTypes = true; 393static const bool kPreloadDexCachesFieldsAndMethods = true; 394 395static const bool kPreloadDexCachesCollectStats = false; 396 397static void preloadDexCachesStatsTotal(DexCacheStats* total) { 398 if (!kPreloadDexCachesCollectStats) { 399 return; 400 } 401 402 for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) { 403 DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe); 404 const DexHeader* pHeader = pDvmDex->pHeader; 405 total->numStrings += pHeader->stringIdsSize; 406 total->numFields += pHeader->fieldIdsSize; 407 total->numMethods += pHeader->methodIdsSize; 408 total->numTypes += pHeader->typeIdsSize; 409 } 410} 411 412static void preloadDexCachesStatsFilled(DexCacheStats* filled) { 413 if (!kPreloadDexCachesCollectStats) { 414 return; 415 } 416 for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) { 417 DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe); 418 const DexHeader* pHeader = pDvmDex->pHeader; 419 for (size_t i = 0; i < pHeader->stringIdsSize; i++) { 420 StringObject* string = dvmDexGetResolvedString(pDvmDex, i); 421 if (string != NULL) { 422 filled->numStrings++; 423 } 424 } 425 for (size_t i = 0; i < pHeader->typeIdsSize; i++) { 426 ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, i); 427 if (clazz != NULL) { 428 filled->numTypes++; 429 } 430 } 431 for (size_t i = 0; i < pHeader->fieldIdsSize; i++) { 432 Field* field = dvmDexGetResolvedField(pDvmDex, i); 433 if (field != NULL) { 434 filled->numFields++; 435 } 436 } 437 for (size_t i = 0; i < pHeader->methodIdsSize; i++) { 438 Method* method = dvmDexGetResolvedMethod(pDvmDex, i); 439 if (method != NULL) { 440 filled->numMethods++; 441 } 442 } 443 } 444} 445 446static void Dalvik_dalvik_system_VMRuntime_preloadDexCaches(const u4* args, JValue* pResult) 447{ 448 if (!kPreloadDexCachesEnabled) { 449 return; 450 } 451 452 DexCacheStats total; 453 DexCacheStats before; 454 if (kPreloadDexCachesCollectStats) { 455 ALOGI("VMRuntime.preloadDexCaches starting"); 456 preloadDexCachesStatsTotal(&total); 457 preloadDexCachesStatsFilled(&before); 458 } 459 460 // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings 461 StringTable strings; 462 if (kPreloadDexCachesStrings) { 463 dvmLockMutex(&gDvm.internLock); 464 dvmHashTableLock(gDvm.literalStrings); 465 for (int i = 0; i < gDvm.literalStrings->tableSize; ++i) { 466 HashEntry *entry = &gDvm.literalStrings->pEntries[i]; 467 if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { 468 preloadDexCachesStringsVisitor(&entry->data, 0, ROOT_INTERNED_STRING, &strings); 469 } 470 } 471 dvmHashTableUnlock(gDvm.literalStrings); 472 dvmUnlockMutex(&gDvm.internLock); 473 } 474 475 for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) { 476 DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe); 477 const DexHeader* pHeader = pDvmDex->pHeader; 478 const DexFile* pDexFile = pDvmDex->pDexFile; 479 480 if (kPreloadDexCachesStrings) { 481 for (size_t i = 0; i < pHeader->stringIdsSize; i++) { 482 preloadDexCachesResolveString(pDvmDex, i, strings); 483 } 484 } 485 486 if (kPreloadDexCachesTypes) { 487 for (size_t i = 0; i < pHeader->typeIdsSize; i++) { 488 preloadDexCachesResolveType(pDvmDex, i); 489 } 490 } 491 492 if (kPreloadDexCachesFieldsAndMethods) { 493 for (size_t classDefIndex = 0; 494 classDefIndex < pHeader->classDefsSize; 495 classDefIndex++) { 496 const DexClassDef* pClassDef = dexGetClassDef(pDexFile, classDefIndex); 497 const u1* pEncodedData = dexGetClassData(pDexFile, pClassDef); 498 UniquePtr<DexClassData> pClassData(dexReadAndVerifyClassData(&pEncodedData, NULL)); 499 if (pClassData.get() == NULL) { 500 continue; 501 } 502 for (uint32_t fieldIndex = 0; 503 fieldIndex < pClassData->header.staticFieldsSize; 504 fieldIndex++) { 505 const DexField* pField = &pClassData->staticFields[fieldIndex]; 506 preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, false); 507 } 508 for (uint32_t fieldIndex = 0; 509 fieldIndex < pClassData->header.instanceFieldsSize; 510 fieldIndex++) { 511 const DexField* pField = &pClassData->instanceFields[fieldIndex]; 512 preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, true); 513 } 514 for (uint32_t methodIndex = 0; 515 methodIndex < pClassData->header.directMethodsSize; 516 methodIndex++) { 517 const DexMethod* pDexMethod = &pClassData->directMethods[methodIndex]; 518 MethodType methodType = (((pDexMethod->accessFlags & ACC_STATIC) != 0) ? 519 METHOD_STATIC : 520 METHOD_DIRECT); 521 preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, methodType); 522 } 523 for (uint32_t methodIndex = 0; 524 methodIndex < pClassData->header.virtualMethodsSize; 525 methodIndex++) { 526 const DexMethod* pDexMethod = &pClassData->virtualMethods[methodIndex]; 527 preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, METHOD_VIRTUAL); 528 } 529 } 530 } 531 } 532 533 if (kPreloadDexCachesCollectStats) { 534 DexCacheStats after; 535 preloadDexCachesStatsFilled(&after); 536 ALOGI("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d", 537 total.numStrings, before.numStrings, after.numStrings); 538 ALOGI("VMRuntime.preloadDexCaches types total=%d before=%d after=%d", 539 total.numTypes, before.numTypes, after.numTypes); 540 ALOGI("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d", 541 total.numFields, before.numFields, after.numFields); 542 ALOGI("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d", 543 total.numMethods, before.numMethods, after.numMethods); 544 ALOGI("VMRuntime.preloadDexCaches finished"); 545 } 546 547 RETURN_VOID(); 548} 549 550const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = { 551 { "addressOf", "(Ljava/lang/Object;)J", 552 Dalvik_dalvik_system_VMRuntime_addressOf }, 553 { "bootClassPath", "()Ljava/lang/String;", 554 Dalvik_dalvik_system_VMRuntime_bootClassPath }, 555 { "classPath", "()Ljava/lang/String;", 556 Dalvik_dalvik_system_VMRuntime_classPath }, 557 { "clearGrowthLimit", "()V", 558 Dalvik_dalvik_system_VMRuntime_clearGrowthLimit }, 559 { "disableJitCompilation", "()V", 560 Dalvik_dalvik_system_VMRuntime_disableJitCompilation }, 561 { "isDebuggerActive", "()Z", 562 Dalvik_dalvik_system_VMRuntime_isDebuggerActive }, 563 { "getTargetHeapUtilization", "()F", 564 Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization }, 565 { "nativeSetTargetHeapUtilization", "(F)V", 566 Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization }, 567 { "newNonMovableArray", "(Ljava/lang/Class;I)Ljava/lang/Object;", 568 Dalvik_dalvik_system_VMRuntime_newNonMovableArray }, 569 { "properties", "()[Ljava/lang/String;", 570 Dalvik_dalvik_system_VMRuntime_properties }, 571 { "setTargetSdkVersion", "(I)V", 572 Dalvik_dalvik_system_VMRuntime_setTargetSdkVersion }, 573 { "startJitCompilation", "()V", 574 Dalvik_dalvik_system_VMRuntime_startJitCompilation }, 575 { "vmVersion", "()Ljava/lang/String;", 576 Dalvik_dalvik_system_VMRuntime_vmVersion }, 577 { "vmLibrary", "()Ljava/lang/String;", 578 Dalvik_dalvik_system_VMRuntime_vmLibrary }, 579 { "registerNativeAllocation", "(I)V", 580 Dalvik_dalvik_system_VMRuntime_registerNativeAllocation }, 581 { "registerNativeFree", "(I)V", 582 Dalvik_dalvik_system_VMRuntime_registerNativeFree }, 583 { "preloadDexCaches", "()V", 584 Dalvik_dalvik_system_VMRuntime_preloadDexCaches }, 585 { NULL, NULL, NULL }, 586}; 587