Home | History | Annotate | Download | only in vm
      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  * Reference table management.
     19  */
     20 #include "Dalvik.h"
     21 
     22 /*
     23  * Initialize a ReferenceTable structure.
     24  */
     25 bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
     26     int maxCount)
     27 {
     28     assert(initialCount > 0);
     29     assert(initialCount <= maxCount);
     30 
     31     pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
     32     if (pRef->table == NULL)
     33         return false;
     34 #ifndef NDEBUG
     35     memset(pRef->table, 0xdd, initialCount * sizeof(Object*));
     36 #endif
     37     pRef->nextEntry = pRef->table;
     38     pRef->allocEntries = initialCount;
     39     pRef->maxEntries = maxCount;
     40 
     41     return true;
     42 }
     43 
     44 /*
     45  * Clears out the contents of a ReferenceTable, freeing allocated storage.
     46  */
     47 void dvmClearReferenceTable(ReferenceTable* pRef)
     48 {
     49     free(pRef->table);
     50     pRef->table = pRef->nextEntry = NULL;
     51     pRef->allocEntries = pRef->maxEntries = -1;
     52 }
     53 
     54 /*
     55  * Add "obj" to "pRef".
     56  */
     57 bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj)
     58 {
     59     assert(obj != NULL);
     60     assert(dvmIsHeapAddress(obj));
     61     assert(pRef->table != NULL);
     62     assert(pRef->allocEntries <= pRef->maxEntries);
     63 
     64     if (pRef->nextEntry == pRef->table + pRef->allocEntries) {
     65         /* reached end of allocated space; did we hit buffer max? */
     66         if (pRef->nextEntry == pRef->table + pRef->maxEntries) {
     67             ALOGW("ReferenceTable overflow (max=%d)", pRef->maxEntries);
     68             return false;
     69         }
     70 
     71         Object** newTable;
     72         int newSize;
     73 
     74         newSize = pRef->allocEntries * 2;
     75         if (newSize > pRef->maxEntries)
     76             newSize = pRef->maxEntries;
     77         assert(newSize > pRef->allocEntries);
     78 
     79         newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
     80         if (newTable == NULL) {
     81             ALOGE("Unable to expand ref table (from %d to %d %d-byte entries)",
     82                 pRef->allocEntries, newSize, sizeof(Object*));
     83             return false;
     84         }
     85         LOGVV("Growing %p from %d to %d", pRef, pRef->allocEntries, newSize);
     86 
     87         /* update entries; adjust "nextEntry" in case memory moved */
     88         pRef->nextEntry = newTable + (pRef->nextEntry - pRef->table);
     89         pRef->table = newTable;
     90         pRef->allocEntries = newSize;
     91     }
     92 
     93     *pRef->nextEntry++ = obj;
     94     return true;
     95 }
     96 
     97 /*
     98  * Returns NULL if not found.
     99  */
    100 Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
    101     Object* obj)
    102 {
    103     Object** ptr;
    104 
    105     ptr = pRef->nextEntry;
    106     while (--ptr >= bottom) {
    107         if (*ptr == obj)
    108             return ptr;
    109     }
    110     return NULL;
    111 }
    112 
    113 /*
    114  * Remove "obj" from "pRef".  We start at the end of the list (where the
    115  * most-recently-added element is), and stop searching for a match after
    116  * examining the element at "bottom".
    117  *
    118  * Most of the time "obj" is at or near the end of the list.  If not, we
    119  * compact it down.
    120  */
    121 bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
    122     Object* obj)
    123 {
    124     Object** ptr;
    125 
    126     assert(pRef->table != NULL);
    127 
    128     /*
    129      * Scan from the most-recently-added entry up to the bottom entry for
    130      * this frame.
    131      */
    132     ptr = dvmFindInReferenceTable(pRef, bottom, obj);
    133     if (ptr == NULL)
    134         return false;
    135 
    136     /*
    137      * Delete the entry.
    138      */
    139     pRef->nextEntry--;
    140     int moveCount = pRef->nextEntry - ptr;
    141     if (moveCount != 0) {
    142         /* remove from middle, slide the rest down */
    143         memmove(ptr, ptr+1, moveCount * sizeof(Object*));
    144         //ALOGV("LREF delete %p, shift %d down", obj, moveCount);
    145     } else {
    146         /* last entry, falls off the end */
    147         //ALOGV("LREF delete %p from end", obj);
    148     }
    149 
    150     return true;
    151 }
    152 
    153 /*
    154  * If "obj" is an array, return the number of elements in the array.
    155  * Otherwise, return zero.
    156  */
    157 static size_t getElementCount(const Object* obj)
    158 {
    159     const ArrayObject* arrayObj = (ArrayObject*) obj;
    160     if (arrayObj == NULL || arrayObj == kClearedJniWeakGlobal ||
    161             arrayObj->clazz == NULL || !dvmIsArray(arrayObj)) {
    162         return 0;
    163     }
    164     return arrayObj->length;
    165 }
    166 
    167 /*
    168  * This is a qsort() callback.  We sort Object* by class, allocation size,
    169  * and then by the Object* itself.
    170  */
    171 static int compareObject(const void* vobj1, const void* vobj2)
    172 {
    173     const Object* obj1 = *((Object* const*) vobj1);
    174     const Object* obj2 = *((Object* const*) vobj2);
    175 
    176     // Ensure null references and cleared jweaks appear at the end.
    177     if (obj1 == NULL) {
    178         if (obj2 == NULL) {
    179             return 0;
    180         } else {
    181             return 1;
    182         }
    183     } else if (obj2 == NULL) {
    184         return -1;
    185     }
    186     if (obj1 == kClearedJniWeakGlobal) {
    187         if (obj2 == kClearedJniWeakGlobal) {
    188             return 0;
    189         } else {
    190             return 1;
    191         }
    192     } else if (obj2 == kClearedJniWeakGlobal) {
    193         return -1;
    194     }
    195 
    196     if (obj1->clazz != obj2->clazz) {
    197         return (u1*)obj1->clazz - (u1*)obj2->clazz;
    198     } else {
    199         size_t count1 = getElementCount(obj1);
    200         size_t count2 = getElementCount(obj2);
    201         if (count1 != count2) {
    202             return count1 - count2;
    203         } else {
    204             return (u1*)obj1 - (u1*)obj2;
    205         }
    206     }
    207 }
    208 
    209 /*
    210  * Log an object with some additional info.
    211  *
    212  * Pass in the number of elements in the array (or 0 if this is not an
    213  * array object), and the number of additional objects that are identical
    214  * or equivalent to the original.
    215  */
    216 static void logSummaryLine(const Object* obj, size_t elems, int identical, int equiv)
    217 {
    218     if (obj == NULL) {
    219         ALOGW("    NULL reference (count=%d)", equiv);
    220         return;
    221     }
    222     if (obj == kClearedJniWeakGlobal) {
    223         ALOGW("    cleared jweak (count=%d)", equiv);
    224         return;
    225     }
    226 
    227     std::string className(dvmHumanReadableType(obj));
    228     if (obj->clazz == gDvm.classJavaLangClass) {
    229         // We're summarizing multiple instances, so using the exemplar
    230         // Class' type parameter here would be misleading.
    231         className = "java.lang.Class";
    232     }
    233     if (elems != 0) {
    234         StringAppendF(&className, " (%zd elements)", elems);
    235     }
    236 
    237     size_t total = identical + equiv + 1;
    238     std::string msg(StringPrintf("%5d of %s", total, className.c_str()));
    239     if (identical + equiv != 0) {
    240         StringAppendF(&msg, " (%d unique instances)", equiv + 1);
    241     }
    242     ALOGW("    %s", msg.c_str());
    243 }
    244 
    245 /*
    246  * Dump a summary of an array of references to the log file.
    247  *
    248  * This is used to dump the contents of ReferenceTable and IndirectRefTable
    249  * structs.
    250  */
    251 void dvmDumpReferenceTableContents(Object* const* refs, size_t count,
    252     const char* descr)
    253 {
    254     ALOGW("%s reference table (%p) dump:", descr, refs);
    255 
    256     if (count == 0) {
    257         ALOGW("  (empty)");
    258         return;
    259     }
    260 
    261     // Dump the most recent N entries.
    262     const size_t kLast = 10;
    263     int first = count - kLast;
    264     if (first < 0) {
    265         first = 0;
    266     }
    267     ALOGW("  Last %d entries (of %d):", (count - first), count);
    268     for (int idx = count - 1; idx >= first; --idx) {
    269         const Object* ref = refs[idx];
    270         if (ref == NULL) {
    271             continue;
    272         }
    273         if (ref == kClearedJniWeakGlobal) {
    274             ALOGW("    %5d: cleared jweak", idx);
    275             continue;
    276         }
    277         if (ref->clazz == NULL) {
    278             // should only be possible right after a plain dvmMalloc().
    279             size_t size = dvmObjectSizeInHeap(ref);
    280             ALOGW("    %5d: %p (raw) (%zd bytes)", idx, ref, size);
    281             continue;
    282         }
    283 
    284         std::string className(dvmHumanReadableType(ref));
    285 
    286         std::string extras;
    287         size_t elems = getElementCount(ref);
    288         if (elems != 0) {
    289             StringAppendF(&extras, " (%zd elements)", elems);
    290         } else if (ref->clazz == gDvm.classJavaLangString) {
    291             const StringObject* str =
    292                     reinterpret_cast<const StringObject*>(ref);
    293             extras += " \"";
    294             size_t count = 0;
    295             char* s = dvmCreateCstrFromString(str);
    296             char* p = s;
    297             for (; *p && count < 16; ++p, ++count) {
    298                 extras += *p;
    299             }
    300             if (*p == 0) {
    301                 extras += "\"";
    302             } else {
    303                 StringAppendF(&extras, "... (%d chars)", str->length());
    304             }
    305             free(s);
    306         }
    307         ALOGW("    %5d: %p %s%s", idx, ref, className.c_str(), extras.c_str());
    308     }
    309 
    310     // Make a copy of the table, and sort it.
    311     Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
    312     if (tableCopy == NULL) {
    313         ALOGE("Unable to copy table with %d elements", count);
    314         return;
    315     }
    316     memcpy(tableCopy, refs, sizeof(Object*) * count);
    317     qsort(tableCopy, count, sizeof(Object*), compareObject);
    318     refs = tableCopy;       // use sorted list
    319 
    320     // Remove any uninteresting stuff from the list. The sort moved them all to the end.
    321     while (count > 0 && refs[count-1] == NULL) {
    322         --count;
    323     }
    324     while (count > 0 && refs[count-1] == kClearedJniWeakGlobal) {
    325         --count;
    326     }
    327     if (count == 0) {
    328         return;
    329     }
    330 
    331     // Dump a summary of the whole table.
    332     ALOGW("  Summary:");
    333     size_t equiv, identical;
    334     equiv = identical = 0;
    335     size_t idx;
    336     size_t elems;
    337     for (idx = 1; idx < count; idx++) {
    338         elems = getElementCount(refs[idx-1]);
    339 
    340         if (refs[idx] == refs[idx-1]) {
    341             // same reference, added more than once.
    342             identical++;
    343         } else if (refs[idx]->clazz == refs[idx-1]->clazz &&
    344             getElementCount(refs[idx]) == elems)
    345         {
    346             // same class / element count, different object.
    347             equiv++;
    348         } else {
    349             // different class.
    350             logSummaryLine(refs[idx-1], elems, identical, equiv);
    351             equiv = identical = 0;
    352         }
    353     }
    354 
    355     // Handle the last entry (everything above outputs refs[i-1]).
    356     elems = getElementCount(refs[idx-1]);
    357     logSummaryLine(refs[count-1], elems, identical, equiv);
    358 
    359     free(tableCopy);
    360 }
    361 
    362 /*
    363  * Dump the contents of a ReferenceTable to the log.
    364  */
    365 void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
    366 {
    367     dvmDumpReferenceTableContents(pRef->table, dvmReferenceTableEntries(pRef),
    368         descr);
    369 }
    370