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 <sys/mman.h> /* for PROT_* */ 18 19 #include "Dalvik.h" 20 #include "alloc/HeapBitmap.h" 21 #include "alloc/HeapBitmapInlines.h" 22 #include "alloc/HeapSource.h" 23 #include "alloc/Visit.h" 24 25 /* 26 * Maintain a card table from the the write barrier. All writes of 27 * non-NULL values to heap addresses should go through an entry in 28 * WriteBarrier, and from there to here. 29 * 30 * The heap is divided into "cards" of GC_CARD_SIZE bytes, as 31 * determined by GC_CARD_SHIFT. The card table contains one byte of 32 * data per card, to be used by the GC. The value of the byte will be 33 * one of GC_CARD_CLEAN or GC_CARD_DIRTY. 34 * 35 * After any store of a non-NULL object pointer into a heap object, 36 * code is obliged to mark the card dirty. The setters in 37 * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The 38 * JIT and fast interpreters also contain code to mark cards as dirty. 39 * 40 * The card table's base [the "biased card table"] gets set to a 41 * rather strange value. In order to keep the JIT from having to 42 * fabricate or load GC_DIRTY_CARD to store into the card table, 43 * biased base is within the mmap allocation at a point where it's low 44 * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details. 45 */ 46 47 /* 48 * Initializes the card table; must be called before any other 49 * dvmCardTable*() functions. 50 */ 51 bool dvmCardTableStartup(size_t heapMaximumSize) 52 { 53 size_t length; 54 void *allocBase; 55 u1 *biasedBase; 56 GcHeap *gcHeap = gDvm.gcHeap; 57 void *heapBase = dvmHeapSourceGetBase(); 58 assert(gcHeap != NULL); 59 assert(heapBase != NULL); 60 61 /* Set up the card table */ 62 length = heapMaximumSize / GC_CARD_SIZE; 63 /* Allocate an extra 256 bytes to allow fixed low-byte of base */ 64 allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE, 65 "dalvik-card-table"); 66 if (allocBase == NULL) { 67 return false; 68 } 69 gcHeap->cardTableBase = (u1*)allocBase; 70 gcHeap->cardTableLength = length; 71 gcHeap->cardTableOffset = 0; 72 /* All zeros is the correct initial value; all clean. */ 73 assert(GC_CARD_CLEAN == 0); 74 75 biasedBase = (u1 *)((uintptr_t)allocBase - 76 ((uintptr_t)heapBase >> GC_CARD_SHIFT)); 77 if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) { 78 int offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff); 79 gcHeap->cardTableOffset = offset + (offset < 0 ? 0x100 : 0); 80 biasedBase += gcHeap->cardTableOffset; 81 } 82 assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY); 83 gDvm.biasedCardTableBase = biasedBase; 84 85 return true; 86 } 87 88 /* 89 * Tears down the entire CardTable. 90 */ 91 void dvmCardTableShutdown() 92 { 93 gDvm.biasedCardTableBase = NULL; 94 munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength); 95 } 96 97 void dvmClearCardTable() 98 { 99 assert(gDvm.gcHeap->cardTableBase != NULL); 100 memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength); 101 } 102 103 /* 104 * Returns true iff the address is within the bounds of the card table. 105 */ 106 bool dvmIsValidCard(const u1 *cardAddr) 107 { 108 GcHeap *h = gDvm.gcHeap; 109 u1* begin = h->cardTableBase + h->cardTableOffset; 110 u1* end = &begin[h->cardTableLength]; 111 return cardAddr >= begin && cardAddr < end; 112 } 113 114 /* 115 * Returns the address of the relevent byte in the card table, given 116 * an address on the heap. 117 */ 118 u1 *dvmCardFromAddr(const void *addr) 119 { 120 u1 *biasedBase = gDvm.biasedCardTableBase; 121 u1 *cardAddr = biasedBase + ((uintptr_t)addr >> GC_CARD_SHIFT); 122 assert(dvmIsValidCard(cardAddr)); 123 return cardAddr; 124 } 125 126 /* 127 * Returns the first address in the heap which maps to this card. 128 */ 129 void *dvmAddrFromCard(const u1 *cardAddr) 130 { 131 assert(dvmIsValidCard(cardAddr)); 132 uintptr_t offset = cardAddr - gDvm.biasedCardTableBase; 133 return (void *)(offset << GC_CARD_SHIFT); 134 } 135 136 /* 137 * Dirties the card for the given address. 138 */ 139 void dvmMarkCard(const void *addr) 140 { 141 u1 *cardAddr = dvmCardFromAddr(addr); 142 *cardAddr = GC_CARD_DIRTY; 143 } 144 145 /* 146 * Returns true if the object is on a dirty card. 147 */ 148 static bool isObjectDirty(const Object *obj) 149 { 150 assert(obj != NULL); 151 assert(dvmIsValidObject(obj)); 152 u1 *card = dvmCardFromAddr(obj); 153 return *card == GC_CARD_DIRTY; 154 } 155 156 /* 157 * Context structure for verifying the card table. 158 */ 159 struct WhiteReferenceCounter { 160 HeapBitmap *markBits; 161 size_t whiteRefs; 162 }; 163 164 /* 165 * Visitor that counts white referents. 166 */ 167 static void countWhiteReferenceVisitor(void *addr, void *arg) 168 { 169 WhiteReferenceCounter *ctx; 170 Object *obj; 171 172 assert(addr != NULL); 173 assert(arg != NULL); 174 obj = *(Object **)addr; 175 if (obj == NULL) { 176 return; 177 } 178 assert(dvmIsValidObject(obj)); 179 ctx = (WhiteReferenceCounter *)arg; 180 if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) { 181 return; 182 } 183 ctx->whiteRefs += 1; 184 } 185 186 /* 187 * Visitor that logs white references. 188 */ 189 static void dumpWhiteReferenceVisitor(void *addr, void *arg) 190 { 191 WhiteReferenceCounter *ctx; 192 Object *obj; 193 194 assert(addr != NULL); 195 assert(arg != NULL); 196 obj = *(Object **)addr; 197 if (obj == NULL) { 198 return; 199 } 200 assert(dvmIsValidObject(obj)); 201 ctx = (WhiteReferenceCounter*)arg; 202 if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) { 203 return; 204 } 205 LOGE("object %p is white", obj); 206 } 207 208 /* 209 * Visitor that signals the caller when a matching reference is found. 210 */ 211 static void dumpReferencesVisitor(void *pObj, void *arg) 212 { 213 Object *obj = *(Object **)pObj; 214 Object *lookingFor = *(Object **)arg; 215 if (lookingFor != NULL && lookingFor == obj) { 216 *(Object **)arg = NULL; 217 } 218 } 219 220 static void dumpReferencesCallback(Object *obj, void *arg) 221 { 222 if (obj == (Object *)arg) { 223 return; 224 } 225 dvmVisitObject(dumpReferencesVisitor, obj, &arg); 226 if (arg == NULL) { 227 LOGD("Found %p in the heap @ %p", arg, obj); 228 dvmDumpObject(obj); 229 } 230 } 231 232 /* 233 * Root visitor that looks for matching references. 234 */ 235 static void dumpReferencesRootVisitor(void *ptr, u4 threadId, 236 RootType type, void *arg) 237 { 238 Object *obj = *(Object **)ptr; 239 Object *lookingFor = *(Object **)arg; 240 if (obj == lookingFor) { 241 LOGD("Found %p in a root @ %p", arg, ptr); 242 } 243 } 244 245 /* 246 * Invokes visitors to search for references to an object. 247 */ 248 static void dumpReferences(const Object *obj) 249 { 250 HeapBitmap *bitmap = dvmHeapSourceGetLiveBits(); 251 void *arg = (void *)obj; 252 dvmVisitRoots(dumpReferencesRootVisitor, arg); 253 dvmHeapBitmapWalk(bitmap, dumpReferencesCallback, arg); 254 } 255 256 /* 257 * Returns true if the given object is a reference object and the 258 * just the referent is unmarked. 259 */ 260 static bool isReferentUnmarked(const Object *obj, 261 const WhiteReferenceCounter* ctx) 262 { 263 assert(obj != NULL); 264 assert(obj->clazz != NULL); 265 assert(ctx != NULL); 266 if (ctx->whiteRefs != 1) { 267 return false; 268 } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) { 269 size_t offset = gDvm.offJavaLangRefReference_referent; 270 const Object *referent = dvmGetFieldObject(obj, offset); 271 return !dvmHeapBitmapIsObjectBitSet(ctx->markBits, referent); 272 } else { 273 return false; 274 } 275 } 276 277 /* 278 * Returns true if the given object is a string and has been interned 279 * by the user. 280 */ 281 static bool isWeakInternedString(const Object *obj) 282 { 283 assert(obj != NULL); 284 if (obj->clazz == gDvm.classJavaLangString) { 285 return dvmIsWeakInternedString((StringObject *)obj); 286 } else { 287 return false; 288 } 289 } 290 291 /* 292 * Returns true if the given object has been pushed on the mark stack 293 * by root marking. 294 */ 295 static bool isPushedOnMarkStack(const Object *obj) 296 { 297 GcMarkStack *stack = &gDvm.gcHeap->markContext.stack; 298 for (const Object **ptr = stack->base; ptr < stack->top; ++ptr) { 299 if (*ptr == obj) { 300 return true; 301 } 302 } 303 return false; 304 } 305 306 /* 307 * Callback applied to marked objects. If the object is gray and on 308 * an unmarked card an error is logged and the VM is aborted. Card 309 * table verification occurs between root marking and weak reference 310 * processing. We treat objects marked from the roots and weak 311 * references specially as it is permissible for these objects to be 312 * gray and on an unmarked card. 313 */ 314 static void verifyCardTableCallback(Object *obj, void *arg) 315 { 316 WhiteReferenceCounter ctx = { (HeapBitmap *)arg, 0 }; 317 318 dvmVisitObject(countWhiteReferenceVisitor, obj, &ctx); 319 if (ctx.whiteRefs == 0) { 320 return; 321 } else if (isObjectDirty(obj)) { 322 return; 323 } else if (isReferentUnmarked(obj, &ctx)) { 324 return; 325 } else if (isWeakInternedString(obj)) { 326 return; 327 } else if (isPushedOnMarkStack(obj)) { 328 return; 329 } else { 330 LOGE("Verify failed, object %p is gray and on an unmarked card", obj); 331 dvmDumpObject(obj); 332 dvmVisitObject(dumpWhiteReferenceVisitor, obj, &ctx); 333 dumpReferences(obj); 334 dvmAbort(); 335 } 336 } 337 338 /* 339 * Verifies that gray objects are on a dirty card. 340 */ 341 void dvmVerifyCardTable() 342 { 343 HeapBitmap *markBits = gDvm.gcHeap->markContext.bitmap; 344 dvmHeapBitmapWalk(markBits, verifyCardTableCallback, markBits); 345 } 346