Home | History | Annotate | Download | only in alloc
      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