| /* |
| * Copyright (C) 2010 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.h" |
| #include "alloc/HeapBitmap.h" |
| #include "alloc/HeapSource.h" |
| #include "alloc/Verify.h" |
| #include "alloc/Visit.h" |
| |
| /* |
| * Helper routine for verifyRefernce that masks low-tag bits before |
| * applying verification checks. TODO: eliminate the use of low-tag |
| * bits and move this code into verifyReference. |
| */ |
| static void verifyReferenceUnmask(const void *addr, uintptr_t mask) |
| { |
| const Object *obj; |
| uintptr_t tmp; |
| bool isValid; |
| |
| tmp = (uintptr_t)*(const Object **)addr; |
| obj = (const Object *)(tmp & ~mask); |
| if (obj == NULL) { |
| isValid = true; |
| } else { |
| isValid = dvmIsValidObject(obj); |
| } |
| if (!isValid) { |
| LOGE("Verify of object %p @ %p failed", obj, addr); |
| dvmAbort(); |
| } |
| } |
| |
| /* |
| * Assertion that the given reference points to a valid object. |
| */ |
| static void verifyReference(const void *addr) |
| { |
| verifyReferenceUnmask(addr, 0); |
| } |
| |
| static void visitorCallback(void *addr, void *arg) |
| { |
| verifyReference(addr); |
| } |
| |
| /* |
| * Verifies an object reference. Determines the type of the reference |
| * and dispatches to a specialized verification routine. |
| */ |
| void dvmVerifyObject(const Object *obj) |
| { |
| dvmVisitObject(visitorCallback, (Object *)obj, NULL); |
| } |
| |
| /* |
| * Helper function to call dvmVerifyObject from a bitmap walker. |
| */ |
| static bool verifyBitmapCallback(size_t numPtrs, void **ptrs, |
| const void *finger, void *arg) |
| { |
| size_t i; |
| |
| for (i = 0; i < numPtrs; i++) { |
| dvmVerifyObject(*ptrs++); |
| } |
| return true; |
| } |
| |
| /* |
| * Verifies the object references in a heap bitmap. Assumes the VM is |
| * suspended. |
| */ |
| void dvmVerifyBitmap(const HeapBitmap *bitmap) |
| { |
| /* TODO: check that locks are held and the VM is suspended. */ |
| dvmHeapBitmapWalk(bitmap, verifyBitmapCallback, NULL); |
| } |
| |
| /* |
| * Applies a verification function to all present values in the hash table. |
| */ |
| static void verifyHashTable(HashTable *table, |
| void (*callback)(const void *arg)) |
| { |
| int i; |
| |
| assert(table != NULL); |
| assert(callback != NULL); |
| dvmHashTableLock(table); |
| for (i = 0; i < table->tableSize; ++i) { |
| const HashEntry *entry = &table->pEntries[i]; |
| if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { |
| (*callback)(&entry->data); |
| } |
| } |
| dvmHashTableUnlock(table); |
| } |
| |
| /* |
| * Applies the verify routine to the given object. |
| */ |
| static void verifyStringReference(const void *arg) |
| { |
| assert(arg != NULL); |
| verifyReferenceUnmask(arg, 0x1); |
| } |
| |
| /* |
| * Verifies all entries in the reference table. |
| */ |
| static void verifyReferenceTable(const ReferenceTable *table) |
| { |
| Object **entry; |
| |
| assert(table != NULL); |
| for (entry = table->table; entry < table->nextEntry; ++entry) { |
| assert(entry != NULL); |
| verifyReference(entry); |
| } |
| } |
| |
| /* |
| * Verifies a large heap reference table. These objects are list |
| * heads. As such, it is valid for table to be NULL. |
| */ |
| static void verifyLargeHeapRefTable(const LargeHeapRefTable *table) |
| { |
| for (; table != NULL; table = table->next) { |
| verifyReferenceTable(&table->refs); |
| } |
| } |
| |
| /* |
| * Verifies all stack slots. TODO: verify native methods. |
| */ |
| static void verifyThreadStack(const Thread *thread) |
| { |
| const StackSaveArea *saveArea; |
| const u4 *framePtr; |
| |
| assert(thread != NULL); |
| framePtr = (const u4 *)thread->curFrame; |
| for (; framePtr != NULL; framePtr = saveArea->prevFrame) { |
| Method *method; |
| saveArea = SAVEAREA_FROM_FP(framePtr); |
| method = (Method *)saveArea->method; |
| if (method != NULL && !dvmIsNativeMethod(method)) { |
| const RegisterMap* pMap = dvmGetExpandedRegisterMap(method); |
| const u1* regVector = NULL; |
| int i; |
| |
| if (pMap != NULL) { |
| /* found map, get registers for this address */ |
| int addr = saveArea->xtra.currentPc - method->insns; |
| regVector = dvmRegisterMapGetLine(pMap, addr); |
| } |
| if (regVector == NULL) { |
| /* |
| * Either there was no register map or there is no |
| * info for the current PC. Perform a conservative |
| * scan. |
| */ |
| for (i = 0; i < method->registersSize; ++i) { |
| if (dvmIsValidObject((Object *)framePtr[i])) { |
| verifyReference(&framePtr[i]); |
| } |
| } |
| } else { |
| /* |
| * Precise scan. v0 is at the lowest address on the |
| * interpreted stack, and is the first bit in the |
| * register vector, so we can walk through the |
| * register map and memory in the same direction. |
| * |
| * A '1' bit indicates a live reference. |
| */ |
| u2 bits = 1 << 1; |
| for (i = 0; i < method->registersSize; ++i) { |
| bits >>= 1; |
| if (bits == 1) { |
| /* set bit 9 so we can tell when we're empty */ |
| bits = *regVector++ | 0x0100; |
| } |
| if ((bits & 0x1) != 0) { |
| /* |
| * Register is marked as live, it's a valid root. |
| */ |
| verifyReference(&framePtr[i]); |
| } |
| } |
| dvmReleaseRegisterMapLine(pMap, regVector); |
| } |
| } |
| /* |
| * Don't fall into an infinite loop if things get corrupted. |
| */ |
| assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr || |
| saveArea->prevFrame == NULL); |
| } |
| } |
| |
| /* |
| * Verifies all roots associated with a thread. |
| */ |
| static void verifyThread(const Thread *thread) |
| { |
| assert(thread != NULL); |
| assert(thread->status != THREAD_RUNNING || |
| thread->isSuspended || |
| thread == dvmThreadSelf()); |
| LOGV("Entering verifyThread(thread=%p)", thread); |
| verifyReference(&thread->threadObj); |
| verifyReference(&thread->exception); |
| verifyReferenceTable(&thread->internalLocalRefTable); |
| verifyReferenceTable(&thread->jniLocalRefTable); |
| if (thread->jniMonitorRefTable.table) { |
| verifyReferenceTable(&thread->jniMonitorRefTable); |
| } |
| verifyThreadStack(thread); |
| LOGV("Exiting verifyThread(thread=%p)", thread); |
| } |
| |
| /* |
| * Verifies all threads on the thread list. |
| */ |
| static void verifyThreads(void) |
| { |
| Thread *thread; |
| |
| dvmLockThreadList(dvmThreadSelf()); |
| thread = gDvm.threadList; |
| while (thread) { |
| verifyThread(thread); |
| thread = thread->next; |
| } |
| dvmUnlockThreadList(); |
| } |
| |
| /* |
| * Verifies roots. TODO: verify all roots. |
| */ |
| void dvmVerifyRoots(void) |
| { |
| verifyHashTable(gDvm.loadedClasses, verifyReference); |
| verifyHashTable(gDvm.dbgRegistry, verifyReference); |
| verifyHashTable(gDvm.internedStrings, verifyStringReference); |
| verifyReferenceTable(&gDvm.jniGlobalRefTable); |
| verifyReferenceTable(&gDvm.jniPinRefTable); |
| verifyLargeHeapRefTable(gDvm.gcHeap->referenceOperations); |
| verifyLargeHeapRefTable(gDvm.gcHeap->pendingFinalizationRefs); |
| verifyThreads(); |
| /* TODO: verify cached global references. */ |
| } |