Home | History | Annotate | Download | only in alloc
      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  * DDM-related heap functions
     18  */
     19 #include <sys/time.h>
     20 #include <time.h>
     21 
     22 #include "Dalvik.h"
     23 #include "alloc/Heap.h"
     24 #include "alloc/HeapInternal.h"
     25 #include "alloc/DdmHeap.h"
     26 #include "alloc/HeapSource.h"
     27 
     28 #define DEFAULT_HEAP_ID  1
     29 
     30 enum HpifWhen {
     31     HPIF_WHEN_NEVER = 0,
     32     HPIF_WHEN_NOW = 1,
     33     HPIF_WHEN_NEXT_GC = 2,
     34     HPIF_WHEN_EVERY_GC = 3
     35 };
     36 
     37 /*
     38  * Chunk HPIF (client --> server)
     39  *
     40  * Heap Info. General information about the heap,
     41  * suitable for a summary display.
     42  *
     43  *   [u4]: number of heaps
     44  *
     45  *   For each heap:
     46  *     [u4]: heap ID
     47  *     [u8]: timestamp in ms since Unix epoch
     48  *     [u1]: capture reason (same as 'when' value from server)
     49  *     [u4]: max heap size in bytes (-Xmx)
     50  *     [u4]: current heap size in bytes
     51  *     [u4]: current number of bytes allocated
     52  *     [u4]: current number of objects allocated
     53  */
     54 #define HPIF_SIZE(numHeaps) \
     55         (sizeof(u4) + (numHeaps) * (5 * sizeof(u4) + sizeof(u1) + sizeof(u8)))
     56 void
     57 dvmDdmSendHeapInfo(int reason, bool shouldLock)
     58 {
     59     struct timeval now;
     60     u8 nowMs;
     61     u1 *buf, *b;
     62 
     63     buf = (u1 *)malloc(HPIF_SIZE(1));
     64     if (buf == NULL) {
     65         return;
     66     }
     67     b = buf;
     68 
     69     /* If there's a one-shot 'when', reset it.
     70      */
     71     if (reason == gDvm.gcHeap->ddmHpifWhen) {
     72         if (shouldLock && ! dvmLockHeap()) {
     73             LOGW("%s(): can't lock heap to clear when\n", __func__);
     74             goto skip_when;
     75         }
     76         if (reason == gDvm.gcHeap->ddmHpifWhen) {
     77             if (gDvm.gcHeap->ddmHpifWhen == HPIF_WHEN_NEXT_GC) {
     78                 gDvm.gcHeap->ddmHpifWhen = HPIF_WHEN_NEVER;
     79             }
     80         }
     81         if (shouldLock) {
     82             dvmUnlockHeap();
     83         }
     84     }
     85 skip_when:
     86 
     87     /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
     88      */
     89     if (gettimeofday(&now, NULL) < 0) {
     90         nowMs = 0;
     91     } else {
     92         nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
     93     }
     94 
     95     /* number of heaps */
     96     set4BE(b, 1); b += 4;
     97 
     98     /* For each heap (of which there is one) */
     99     {
    100         /* heap ID */
    101         set4BE(b, DEFAULT_HEAP_ID); b += 4;
    102 
    103         /* timestamp */
    104         set8BE(b, nowMs); b += 8;
    105 
    106         /* 'when' value */
    107         *b++ = (u1)reason;
    108 
    109         /* max allowed heap size in bytes */
    110         set4BE(b, gDvm.heapSizeMax); b += 4;
    111 
    112         /* current heap size in bytes */
    113         set4BE(b, dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0)); b += 4;
    114 
    115         /* number of bytes allocated */
    116         set4BE(b, dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0)); b += 4;
    117 
    118         /* number of objects allocated */
    119         set4BE(b, dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0)); b += 4;
    120     }
    121     assert((intptr_t)b == (intptr_t)buf + (intptr_t)HPIF_SIZE(1));
    122 
    123     dvmDbgDdmSendChunk(CHUNK_TYPE("HPIF"), b - buf, buf);
    124 }
    125 
    126 bool
    127 dvmDdmHandleHpifChunk(int when)
    128 {
    129     switch (when) {
    130     case HPIF_WHEN_NOW:
    131         dvmDdmSendHeapInfo(when, true);
    132         break;
    133     case HPIF_WHEN_NEVER:
    134     case HPIF_WHEN_NEXT_GC:
    135     case HPIF_WHEN_EVERY_GC:
    136         if (dvmLockHeap()) {
    137             gDvm.gcHeap->ddmHpifWhen = when;
    138             dvmUnlockHeap();
    139         } else {
    140             LOGI("%s(): can't lock heap to set when\n", __func__);
    141             return false;
    142         }
    143         break;
    144     default:
    145         LOGI("%s(): bad when value 0x%08x\n", __func__, when);
    146         return false;
    147     }
    148 
    149     return true;
    150 }
    151 
    152 enum HpsgSolidity {
    153     SOLIDITY_FREE = 0,
    154     SOLIDITY_HARD = 1,
    155     SOLIDITY_SOFT = 2,
    156     SOLIDITY_WEAK = 3,
    157     SOLIDITY_PHANTOM = 4,
    158     SOLIDITY_FINALIZABLE = 5,
    159     SOLIDITY_SWEEP = 6,
    160 };
    161 
    162 enum HpsgKind {
    163     KIND_OBJECT = 0,
    164     KIND_CLASS_OBJECT = 1,
    165     KIND_ARRAY_1 = 2,
    166     KIND_ARRAY_2 = 3,
    167     KIND_ARRAY_4 = 4,
    168     KIND_ARRAY_8 = 5,
    169     KIND_UNKNOWN = 6,
    170     KIND_NATIVE = 7,
    171 };
    172 
    173 #define HPSG_PARTIAL (1<<7)
    174 #define HPSG_STATE(solidity, kind) \
    175     ((u1)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
    176 
    177 typedef struct HeapChunkContext {
    178     u1 *buf;
    179     u1 *p;
    180     u1 *pieceLenField;
    181     size_t bufLen;
    182     size_t totalAllocationUnits;
    183     int type;
    184     bool merge;
    185     bool needHeader;
    186 } HeapChunkContext;
    187 
    188 #define ALLOCATION_UNIT_SIZE 8
    189 
    190 static void
    191 flush_hpsg_chunk(HeapChunkContext *ctx)
    192 {
    193     /* Patch the "length of piece" field.
    194      */
    195     assert(ctx->buf <= ctx->pieceLenField &&
    196             ctx->pieceLenField <= ctx->p);
    197     set4BE(ctx->pieceLenField, ctx->totalAllocationUnits);
    198 
    199     /* Send the chunk.
    200      */
    201     dvmDbgDdmSendChunk(ctx->type, ctx->p - ctx->buf, ctx->buf);
    202 
    203     /* Reset the context.
    204      */
    205     ctx->p = ctx->buf;
    206     ctx->totalAllocationUnits = 0;
    207     ctx->needHeader = true;
    208     ctx->pieceLenField = NULL;
    209 }
    210 
    211 static void
    212 heap_chunk_callback(const void *chunkptr, size_t chunklen,
    213                     const void *userptr, size_t userlen, void *arg)
    214 {
    215     HeapChunkContext *ctx = (HeapChunkContext *)arg;
    216     u1 state;
    217 
    218     UNUSED_PARAMETER(userlen);
    219 
    220     assert((chunklen & (ALLOCATION_UNIT_SIZE-1)) == 0);
    221 
    222     /* Make sure there's enough room left in the buffer.
    223      * We need to use two bytes for every fractional 256
    224      * allocation units used by the chunk.
    225      */
    226     {
    227         size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
    228         size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
    229         if (bytesLeft < needed) {
    230             flush_hpsg_chunk(ctx);
    231         }
    232 
    233         bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
    234         if (bytesLeft < needed) {
    235             LOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)\n",
    236                 chunklen, needed);
    237             return;
    238         }
    239     }
    240 
    241 //TODO: notice when there's a gap and start a new heap, or at least a new range.
    242     if (ctx->needHeader) {
    243         /*
    244          * Start a new HPSx chunk.
    245          */
    246 
    247         /* [u4]: heap ID */
    248         set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4;
    249 
    250         /* [u1]: size of allocation unit, in bytes */
    251         *ctx->p++ = 8;
    252 
    253         /* [u4]: virtual address of segment start */
    254         set4BE(ctx->p, (uintptr_t)chunkptr); ctx->p += 4;
    255 
    256         /* [u4]: offset of this piece (relative to the virtual address) */
    257         set4BE(ctx->p, 0); ctx->p += 4;
    258 
    259         /* [u4]: length of piece, in allocation units
    260          * We won't know this until we're done, so save the offset
    261          * and stuff in a dummy value.
    262          */
    263         ctx->pieceLenField = ctx->p;
    264         set4BE(ctx->p, 0x55555555); ctx->p += 4;
    265 
    266         ctx->needHeader = false;
    267     }
    268 
    269     /* Determine the type of this chunk.
    270      */
    271     if (userptr == NULL) {
    272         /* It's a free chunk.
    273          */
    274         state = HPSG_STATE(SOLIDITY_FREE, 0);
    275     } else {
    276         const Object *obj = userptr;
    277         /* If we're looking at the native heap, we'll just return
    278          * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
    279          */
    280         bool native = ctx->type == CHUNK_TYPE("NHSG");
    281 
    282         /* It's an allocated chunk.  Figure out what it is.
    283          */
    284 //TODO: if ctx.merge, see if this chunk is different from the last chunk.
    285 //      If it's the same, we should combine them.
    286         if (!native && dvmIsValidObject(obj)) {
    287             ClassObject *clazz = obj->clazz;
    288             if (clazz == NULL) {
    289                 /* The object was probably just created
    290                  * but hasn't been initialized yet.
    291                  */
    292                 state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
    293             } else if (clazz == gDvm.classJavaLangClass) {
    294                 state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
    295             } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
    296                 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
    297                     state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
    298                 } else {
    299                     switch (clazz->elementClass->primitiveType) {
    300                     case PRIM_BOOLEAN:
    301                     case PRIM_BYTE:
    302                         state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
    303                         break;
    304                     case PRIM_CHAR:
    305                     case PRIM_SHORT:
    306                         state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
    307                         break;
    308                     case PRIM_INT:
    309                     case PRIM_FLOAT:
    310                         state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
    311                         break;
    312                     case PRIM_DOUBLE:
    313                     case PRIM_LONG:
    314                         state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
    315                         break;
    316                     default:
    317                         assert(!"Unknown GC heap object type");
    318                         state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
    319                         break;
    320                     }
    321                 }
    322             } else {
    323                 state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
    324             }
    325         } else {
    326             obj = NULL; // it's not actually an object
    327             state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
    328         }
    329     }
    330 
    331     /* Write out the chunk description.
    332      */
    333     chunklen /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
    334     ctx->totalAllocationUnits += chunklen;
    335     while (chunklen > 256) {
    336         *ctx->p++ = state | HPSG_PARTIAL;
    337         *ctx->p++ = 255;     // length - 1
    338         chunklen -= 256;
    339     }
    340     *ctx->p++ = state;
    341     *ctx->p++ = chunklen - 1;
    342 }
    343 
    344 enum HpsgWhen {
    345     HPSG_WHEN_NEVER = 0,
    346     HPSG_WHEN_EVERY_GC = 1,
    347 };
    348 enum HpsgWhat {
    349     HPSG_WHAT_MERGED_OBJECTS = 0,
    350     HPSG_WHAT_DISTINCT_OBJECTS = 1,
    351 };
    352 
    353 /*
    354  * Maximum chunk size.  Obtain this from the formula:
    355  *
    356  * (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
    357  */
    358 #define HPSx_CHUNK_SIZE (16384 - 16)
    359 
    360 void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*),void*);
    361 
    362 static void
    363 walkHeap(bool merge, bool native)
    364 {
    365     HeapChunkContext ctx;
    366 
    367     memset(&ctx, 0, sizeof(ctx));
    368     ctx.bufLen = HPSx_CHUNK_SIZE;
    369     ctx.buf = (u1 *)malloc(ctx.bufLen);
    370     if (ctx.buf == NULL) {
    371         return;
    372     }
    373 
    374     ctx.merge = merge;
    375     if (native) {
    376         ctx.type = CHUNK_TYPE("NHSG");
    377     } else {
    378         if (ctx.merge) {
    379             ctx.type = CHUNK_TYPE("HPSG");
    380         } else {
    381             ctx.type = CHUNK_TYPE("HPSO");
    382         }
    383     }
    384 
    385     ctx.p = ctx.buf;
    386     ctx.needHeader = true;
    387     if (native) {
    388         dlmalloc_walk_heap(heap_chunk_callback, (void *)&ctx);
    389     } else {
    390         dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
    391     }
    392     if (ctx.p > ctx.buf) {
    393         flush_hpsg_chunk(&ctx);
    394     }
    395 
    396     free(ctx.buf);
    397 }
    398 
    399 void
    400 dvmDdmSendHeapSegments(bool shouldLock, bool native)
    401 {
    402     u1 heapId[sizeof(u4)];
    403     GcHeap *gcHeap = gDvm.gcHeap;
    404     int when, what;
    405     bool merge;
    406 
    407     /* Don't even grab the lock if there's nothing to do when we're called.
    408      */
    409     if (!native) {
    410         when = gcHeap->ddmHpsgWhen;
    411         what = gcHeap->ddmHpsgWhat;
    412         if (when == HPSG_WHEN_NEVER) {
    413             return;
    414         }
    415     } else {
    416         when = gcHeap->ddmNhsgWhen;
    417         what = gcHeap->ddmNhsgWhat;
    418         if (when == HPSG_WHEN_NEVER) {
    419             return;
    420         }
    421     }
    422     if (shouldLock && !dvmLockHeap()) {
    423         LOGW("Can't lock heap for DDM HPSx dump\n");
    424         return;
    425     }
    426 
    427     /* Figure out what kind of chunks we'll be sending.
    428      */
    429     if (what == HPSG_WHAT_MERGED_OBJECTS) {
    430         merge = true;
    431     } else if (what == HPSG_WHAT_DISTINCT_OBJECTS) {
    432         merge = false;
    433     } else {
    434         assert(!"bad HPSG.what value");
    435         return;
    436     }
    437 
    438     /* First, send a heap start chunk.
    439      */
    440     set4BE(heapId, DEFAULT_HEAP_ID);
    441     dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
    442         sizeof(u4), heapId);
    443 
    444     /* Send a series of heap segment chunks.
    445      */
    446     walkHeap(merge, native);
    447 
    448     /* Finally, send a heap end chunk.
    449      */
    450     dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
    451         sizeof(u4), heapId);
    452 
    453     if (shouldLock) {
    454         dvmUnlockHeap();
    455     }
    456 }
    457 
    458 bool
    459 dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native)
    460 {
    461     LOGI("dvmDdmHandleHpsgChunk(when %d, what %d, heap %d)\n", when, what,
    462          native);
    463     switch (when) {
    464     case HPSG_WHEN_NEVER:
    465     case HPSG_WHEN_EVERY_GC:
    466         break;
    467     default:
    468         LOGI("%s(): bad when value 0x%08x\n", __func__, when);
    469         return false;
    470     }
    471 
    472     switch (what) {
    473     case HPSG_WHAT_MERGED_OBJECTS:
    474     case HPSG_WHAT_DISTINCT_OBJECTS:
    475         break;
    476     default:
    477         LOGI("%s(): bad what value 0x%08x\n", __func__, what);
    478         return false;
    479     }
    480 
    481     if (dvmLockHeap()) {
    482         if (!native) {
    483             gDvm.gcHeap->ddmHpsgWhen = when;
    484             gDvm.gcHeap->ddmHpsgWhat = what;
    485         } else {
    486             gDvm.gcHeap->ddmNhsgWhen = when;
    487             gDvm.gcHeap->ddmNhsgWhat = what;
    488         }
    489 //TODO: if what says we should dump immediately, signal (or do) it from here
    490         dvmUnlockHeap();
    491     } else {
    492         LOGI("%s(): can't lock heap to set when/what\n", __func__);
    493         return false;
    494     }
    495 
    496     return true;
    497 }
    498