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