1 /* 2 * Copyright (C) 2010 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 #include "Dalvik.h" 18 #include "alloc/HeapInternal.h" 19 #include "alloc/Visit.h" 20 #include "alloc/VisitInlines.h" 21 22 /* 23 * Visits all of the reference locations in an object. 24 */ 25 void dvmVisitObject(Visitor *visitor, Object *obj, void *arg) 26 { 27 assert(visitor != NULL); 28 assert(obj != NULL); 29 assert(obj->clazz != NULL); 30 visitObject(visitor, obj, arg); 31 } 32 33 /* 34 * Applies a verification function to all present values in the hash table. 35 */ 36 static void visitHashTable(RootVisitor *visitor, HashTable *table, 37 RootType type, void *arg) 38 { 39 assert(visitor != NULL); 40 assert(table != NULL); 41 dvmHashTableLock(table); 42 for (int i = 0; i < table->tableSize; ++i) { 43 HashEntry *entry = &table->pEntries[i]; 44 if (entry->data != NULL && entry->data != HASH_TOMBSTONE) { 45 (*visitor)(&entry->data, 0, type, arg); 46 } 47 } 48 dvmHashTableUnlock(table); 49 } 50 51 /* 52 * Visits all entries in the reference table. 53 */ 54 static void visitReferenceTable(RootVisitor *visitor, ReferenceTable *table, 55 u4 threadId, RootType type, void *arg) 56 { 57 assert(visitor != NULL); 58 assert(table != NULL); 59 for (Object **entry = table->table; entry < table->nextEntry; ++entry) { 60 assert(entry != NULL); 61 (*visitor)(entry, threadId, type, arg); 62 } 63 } 64 65 /* 66 * Visits all entries in the indirect reference table. 67 */ 68 static void visitIndirectRefTable(RootVisitor *visitor, IndirectRefTable *table, 69 u4 threadId, RootType type, void *arg) 70 { 71 assert(visitor != NULL); 72 assert(table != NULL); 73 typedef IndirectRefTable::iterator It; // TODO: C++0x auto 74 for (It it = table->begin(), end = table->end(); it != end; ++it) { 75 (*visitor)(*it, threadId, type, arg); 76 } 77 } 78 79 /* 80 * Visits all stack slots except those belonging to native method 81 * arguments. 82 */ 83 static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg) 84 { 85 assert(visitor != NULL); 86 assert(thread != NULL); 87 u4 threadId = thread->threadId; 88 const StackSaveArea *saveArea; 89 for (u4 *fp = (u4 *)thread->interpSave.curFrame; 90 fp != NULL; 91 fp = (u4 *)saveArea->prevFrame) { 92 Method *method; 93 saveArea = SAVEAREA_FROM_FP(fp); 94 method = (Method *)saveArea->method; 95 if (method != NULL && !dvmIsNativeMethod(method)) { 96 const RegisterMap* pMap = dvmGetExpandedRegisterMap(method); 97 const u1* regVector = NULL; 98 if (pMap != NULL) { 99 /* found map, get registers for this address */ 100 int addr = saveArea->xtra.currentPc - method->insns; 101 regVector = dvmRegisterMapGetLine(pMap, addr); 102 } 103 if (regVector == NULL) { 104 /* 105 * Either there was no register map or there is no 106 * info for the current PC. Perform a conservative 107 * scan. 108 */ 109 for (size_t i = 0; i < method->registersSize; ++i) { 110 if (dvmIsValidObject((Object *)fp[i])) { 111 (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg); 112 } 113 } 114 } else { 115 /* 116 * Precise scan. v0 is at the lowest address on the 117 * interpreted stack, and is the first bit in the 118 * register vector, so we can walk through the 119 * register map and memory in the same direction. 120 * 121 * A '1' bit indicates a live reference. 122 */ 123 u2 bits = 1 << 1; 124 for (size_t i = 0; i < method->registersSize; ++i) { 125 bits >>= 1; 126 if (bits == 1) { 127 /* set bit 9 so we can tell when we're empty */ 128 bits = *regVector++ | 0x0100; 129 } 130 if ((bits & 0x1) != 0) { 131 /* 132 * Register is marked as live, it's a valid root. 133 */ 134 #if WITH_EXTRA_GC_CHECKS 135 if (fp[i] != 0 && !dvmIsValidObject((Object *)fp[i])) { 136 /* this is very bad */ 137 ALOGE("PGC: invalid ref in reg %d: %#x", 138 method->registersSize - 1 - i, fp[i]); 139 ALOGE("PGC: %s.%s addr %#x", 140 method->clazz->descriptor, method->name, 141 saveArea->xtra.currentPc - method->insns); 142 continue; 143 } 144 #endif 145 (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg); 146 } 147 } 148 dvmReleaseRegisterMapLine(pMap, regVector); 149 } 150 } 151 /* 152 * Don't fall into an infinite loop if things get corrupted. 153 */ 154 assert((uintptr_t)saveArea->prevFrame > (uintptr_t)fp || 155 saveArea->prevFrame == NULL); 156 } 157 } 158 159 /* 160 * Visits all roots associated with a thread. 161 */ 162 static void visitThread(RootVisitor *visitor, Thread *thread, void *arg) 163 { 164 u4 threadId; 165 166 assert(visitor != NULL); 167 assert(thread != NULL); 168 threadId = thread->threadId; 169 (*visitor)(&thread->threadObj, threadId, ROOT_THREAD_OBJECT, arg); 170 (*visitor)(&thread->exception, threadId, ROOT_NATIVE_STACK, arg); 171 visitReferenceTable(visitor, &thread->internalLocalRefTable, threadId, ROOT_NATIVE_STACK, arg); 172 visitIndirectRefTable(visitor, &thread->jniLocalRefTable, threadId, ROOT_JNI_LOCAL, arg); 173 if (thread->jniMonitorRefTable.table != NULL) { 174 visitReferenceTable(visitor, &thread->jniMonitorRefTable, threadId, ROOT_JNI_MONITOR, arg); 175 } 176 visitThreadStack(visitor, thread, arg); 177 } 178 179 /* 180 * Visits all threads on the thread list. 181 */ 182 static void visitThreads(RootVisitor *visitor, void *arg) 183 { 184 Thread *thread; 185 186 assert(visitor != NULL); 187 dvmLockThreadList(dvmThreadSelf()); 188 thread = gDvm.threadList; 189 while (thread) { 190 visitThread(visitor, thread, arg); 191 thread = thread->next; 192 } 193 dvmUnlockThreadList(); 194 } 195 196 static void visitPrimitiveTypes(RootVisitor *visitor, void *arg) 197 { 198 (*visitor)(&gDvm.typeVoid, 0, ROOT_STICKY_CLASS, arg); 199 (*visitor)(&gDvm.typeBoolean, 0, ROOT_STICKY_CLASS, arg); 200 (*visitor)(&gDvm.typeByte, 0, ROOT_STICKY_CLASS, arg); 201 (*visitor)(&gDvm.typeShort, 0, ROOT_STICKY_CLASS, arg); 202 (*visitor)(&gDvm.typeChar, 0, ROOT_STICKY_CLASS, arg); 203 (*visitor)(&gDvm.typeInt, 0, ROOT_STICKY_CLASS, arg); 204 (*visitor)(&gDvm.typeLong, 0, ROOT_STICKY_CLASS, arg); 205 (*visitor)(&gDvm.typeFloat, 0, ROOT_STICKY_CLASS, arg); 206 (*visitor)(&gDvm.typeDouble, 0, ROOT_STICKY_CLASS, arg); 207 } 208 209 /* 210 * Visits roots. TODO: visit cached global references. 211 */ 212 void dvmVisitRoots(RootVisitor *visitor, void *arg) 213 { 214 assert(visitor != NULL); 215 visitHashTable(visitor, gDvm.loadedClasses, ROOT_STICKY_CLASS, arg); 216 visitPrimitiveTypes(visitor, arg); 217 if (gDvm.dbgRegistry != NULL) { 218 visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg); 219 } 220 if (gDvm.literalStrings != NULL) { 221 visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg); 222 } 223 dvmLockMutex(&gDvm.jniGlobalRefLock); 224 visitIndirectRefTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg); 225 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 226 dvmLockMutex(&gDvm.jniPinRefLock); 227 visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg); 228 dvmUnlockMutex(&gDvm.jniPinRefLock); 229 visitThreads(visitor, arg); 230 (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg); 231 (*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg); 232 (*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg); 233 } 234