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 #include <fcntl.h>
     18 #include <stdlib.h>
     19 
     20 #include "Dalvik.h"
     21 #include "HeapInternal.h"
     22 #include "HeapSource.h"
     23 #include "Float12.h"
     24 
     25 int dvmGetHeapDebugInfo(HeapDebugInfoType info)
     26 {
     27     switch (info) {
     28     case kVirtualHeapSize:
     29         return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
     30     case kVirtualHeapAllocated:
     31         return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
     32     default:
     33         return -1;
     34     }
     35 }
     36 
     37 /* Looks up the cmdline for the process and tries to find
     38  * the most descriptive five characters, then inserts the
     39  * short name into the provided event value.
     40  */
     41 #define PROC_NAME_LEN 5
     42 static void insertProcessName(long long *ep)
     43 {
     44     static bool foundRealName = false;
     45     static char name[PROC_NAME_LEN] = { 'X', 'X', 'X', 'X', 'X' };
     46     long long event = *ep;
     47 
     48     if (!foundRealName) {
     49         int fd = open("/proc/self/cmdline", O_RDONLY);
     50         if (fd > 0) {
     51             char buf[128];
     52             ssize_t n = read(fd, buf, sizeof(buf) - 1);
     53             close(fd);
     54             if (n > 0) {
     55                 memset(name, 0, sizeof(name));
     56                 if (n <= PROC_NAME_LEN) {
     57                     // The whole name fits.
     58                     memcpy(name, buf, n);
     59                 } else {
     60                     /* We need to truncate.  The name will look something
     61                      * like "com.android.home".  Favor the characters
     62                      * immediately following the last dot.
     63                      */
     64                     buf[n] = '\0';
     65                     char *dot = strrchr(buf, '.');
     66                     if (dot == NULL) {
     67                         /* Or, look for a slash, in case it's something like
     68                          * "/system/bin/runtime".
     69                          */
     70                         dot = strrchr(buf, '/');
     71                     }
     72                     if (dot != NULL) {
     73                         dot++;  // Skip the dot
     74                         size_t dotlen = strlen(dot);
     75                         if (dotlen < PROC_NAME_LEN) {
     76                             /* Use all available characters.  We know that
     77                              * n > PROC_NAME_LEN from the check above.
     78                              */
     79                             dot -= PROC_NAME_LEN - dotlen;
     80                         }
     81                         strncpy(name, dot, PROC_NAME_LEN);
     82                     } else {
     83                         // No dot; just use the leading characters.
     84                         memcpy(name, buf, PROC_NAME_LEN);
     85                     }
     86                 }
     87                 if (strcmp(buf, "zygote") != 0) {
     88                     /* If the process is no longer called "zygote",
     89                      * cache this name.
     90                      */
     91                     foundRealName = true;
     92                 }
     93             }
     94         }
     95     }
     96 
     97     event &= ~(0xffffffffffLL << 24);
     98     event |= (long long)name[0] << 56;
     99     event |= (long long)name[1] << 48;
    100     event |= (long long)name[2] << 40;
    101     event |= (long long)name[3] << 32;
    102     event |= (long long)name[4] << 24;
    103 
    104     *ep = event;
    105 }
    106 
    107 // See device/data/etc/event-log-tags
    108 #define EVENT_LOG_TAG_dvm_gc_info 20001
    109 #define EVENT_LOG_TAG_dvm_gc_madvise_info 20002
    110 
    111 void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs)
    112 {
    113     const GcHeap *gcHeap = gDvm.gcHeap;
    114     size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT],
    115            perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT],
    116            perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT],
    117            perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT];
    118     unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4];
    119     size_t actualSize, allowedSize, numAllocated, sizeAllocated;
    120     size_t i;
    121     size_t softLimit = dvmHeapSourceGetIdealFootprint();
    122     size_t nHeaps = dvmHeapSourceGetNumHeaps();
    123 
    124     /* Enough to quiet down gcc for unitialized variable check */
    125     perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] =
    126                            perHeapSizeAllocated[0] = 0;
    127     actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize,
    128                                        HEAP_SOURCE_MAX_HEAP_COUNT);
    129     allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT,
    130                       perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT);
    131     numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED,
    132                       perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
    133     sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED,
    134                       perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
    135 
    136     /*
    137      * Construct the the first 64-bit value to write to the log.
    138      * Global information:
    139      *
    140      * [63   ] Must be zero
    141      * [62-24] ASCII process identifier
    142      * [23-12] GC time in ms
    143      * [11- 0] Bytes freed
    144      *
    145      */
    146     long long event0;
    147     event0 = 0LL << 63 |
    148             (long long)intToFloat12(gcTimeMs) << 12 |
    149             (long long)intToFloat12(sizeFreed);
    150     insertProcessName(&event0);
    151 
    152     /*
    153      * Aggregated heap stats:
    154      *
    155      * [63-62] 10
    156      * [61-60] Reserved; must be zero
    157      * [59-48] Objects freed
    158      * [47-36] Actual size (current footprint)
    159      * [35-24] Allowed size (current hard max)
    160      * [23-12] Objects allocated
    161      * [11- 0] Bytes allocated
    162      */
    163     long long event1;
    164     event1 = 2LL << 62 |
    165             (long long)intToFloat12(numFreed) << 48 |
    166             (long long)intToFloat12(actualSize) << 36 |
    167             (long long)intToFloat12(allowedSize) << 24 |
    168             (long long)intToFloat12(numAllocated) << 12 |
    169             (long long)intToFloat12(sizeAllocated);
    170 
    171     /*
    172      * Report the current state of the zygote heap(s).
    173      *
    174      * The active heap is always heap[0].  We can be in one of three states
    175      * at present:
    176      *
    177      *  (1) Still in the zygote.  Zygote using heap[0].
    178      *  (2) In the zygote, when the first child is started.  We created a
    179      *      new heap just before the first fork() call, so the original
    180      *      "zygote heap" is now heap[1], and we have a small heap[0] for
    181      *      anything we do from here on.
    182      *  (3) In an app process.  The app gets a new heap[0], and can also
    183      *      see the two zygote heaps [1] and [2] (probably unwise to
    184      *      assume any specific ordering).
    185      *
    186      * So if nHeaps == 1, we want the stats from heap[0]; else we want
    187      * the sum of the values from heap[1] to heap[nHeaps-1].
    188      *
    189      *
    190      * Zygote heap stats (except for the soft limit, which belongs to the
    191      * active heap):
    192      *
    193      * [63-62] 11
    194      * [61-60] Reserved; must be zero
    195      * [59-48] Soft Limit (for the active heap)
    196      * [47-36] Actual size (current footprint)
    197      * [35-24] Allowed size (current hard max)
    198      * [23-12] Objects allocated
    199      * [11- 0] Bytes allocated
    200      */
    201     long long event2;
    202     size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated;
    203     int firstHeap = (nHeaps == 1) ? 0 : 1;
    204     size_t hh;
    205 
    206     zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0;
    207     for (hh = firstHeap; hh < nHeaps; hh++) {
    208         zActualSize += perHeapActualSize[hh];
    209         zAllowedSize += perHeapAllowedSize[hh];
    210         zNumAllocated += perHeapNumAllocated[hh];
    211         zSizeAllocated += perHeapSizeAllocated[hh];
    212     }
    213     event2 = 3LL << 62 |
    214             (long long)intToFloat12(softLimit) << 48 |
    215             (long long)intToFloat12(zActualSize) << 36 |
    216             (long long)intToFloat12(zAllowedSize) << 24 |
    217             (long long)intToFloat12(zNumAllocated) << 12 |
    218             (long long)intToFloat12(zSizeAllocated);
    219 
    220     /*
    221      * Report the current external allocation stats and the native heap
    222      * summary.
    223      *
    224      * [63-48] Reserved; must be zero (TODO: put new data in these slots)
    225      * [47-36] dlmalloc_footprint
    226      * [35-24] mallinfo: total allocated space
    227      * [23-12] External byte limit
    228      * [11- 0] External bytes allocated
    229      */
    230     long long event3;
    231     size_t externalLimit, externalBytesAllocated;
    232     size_t uordblks, footprint;
    233 
    234 #if 0
    235     /*
    236      * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost
    237      * of a GC, so it's not horribly expensive but it's not free either.
    238      */
    239     extern size_t dlmalloc_footprint(void);
    240     struct mallinfo mi;
    241     //u8 start, end;
    242 
    243     //start = dvmGetRelativeTimeNsec();
    244     mi = mallinfo();
    245     uordblks = mi.uordblks;
    246     footprint = dlmalloc_footprint();
    247     //end = dvmGetRelativeTimeNsec();
    248     //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n",
    249     //    (int)((end - start) / 1000), mi.uordblks, footprint);
    250 #else
    251     uordblks = footprint = 0;
    252 #endif
    253 
    254     externalLimit =
    255             dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
    256     externalBytesAllocated =
    257             dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
    258     event3 =
    259             (long long)intToFloat12(footprint) << 36 |
    260             (long long)intToFloat12(uordblks) << 24 |
    261             (long long)intToFloat12(externalLimit) << 12 |
    262             (long long)intToFloat12(externalBytesAllocated);
    263 
    264     /* Build the event data.
    265      * [ 0: 0] item count (4)
    266      * [ 1: 1] EVENT_TYPE_LONG
    267      * [ 2: 9] event0
    268      * [10:10] EVENT_TYPE_LONG
    269      * [11:18] event1
    270      * [19:19] EVENT_TYPE_LONG
    271      * [20:27] event2
    272      * [28:28] EVENT_TYPE_LONG
    273      * [29:36] event2
    274      */
    275     unsigned char *c = eventBuf;
    276     *c++ = 4;
    277     *c++ = EVENT_TYPE_LONG;
    278     memcpy(c, &event0, sizeof(event0));
    279     c += sizeof(event0);
    280     *c++ = EVENT_TYPE_LONG;
    281     memcpy(c, &event1, sizeof(event1));
    282     c += sizeof(event1);
    283     *c++ = EVENT_TYPE_LONG;
    284     memcpy(c, &event2, sizeof(event2));
    285     c += sizeof(event2);
    286     *c++ = EVENT_TYPE_LONG;
    287     memcpy(c, &event3, sizeof(event3));
    288 
    289     (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST,
    290             eventBuf, sizeof(eventBuf));
    291 }
    292 
    293 void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen)
    294 {
    295     unsigned char eventBuf[1 + (1 + sizeof(int)) * 2];
    296     size_t total, zyg;
    297     size_t firstHeap, i;
    298     size_t nHeaps = dvmHeapSourceGetNumHeaps();
    299 
    300     assert(arrayLen >= nHeaps);
    301 
    302     firstHeap = nHeaps > 1 ? 1 : 0;
    303     total = 0;
    304     zyg = 0;
    305     for (i = 0; i < nHeaps; i++) {
    306         total += madvisedSizes[i];
    307         if (i >= firstHeap) {
    308             zyg += madvisedSizes[i];
    309         }
    310     }
    311 
    312     /* Build the event data.
    313      * [ 0: 0] item count (2)
    314      * [ 1: 1] EVENT_TYPE_INT
    315      * [ 2: 5] total madvise byte count
    316      * [ 6: 6] EVENT_TYPE_INT
    317      * [ 7:10] zygote heap madvise byte count
    318      */
    319     unsigned char *c = eventBuf;
    320     *c++ = 2;
    321     *c++ = EVENT_TYPE_INT;
    322     memcpy(c, &total, sizeof(total));
    323     c += sizeof(total);
    324     *c++ = EVENT_TYPE_INT;
    325     memcpy(c, &zyg, sizeof(zyg));
    326     c += sizeof(zyg);
    327 
    328     (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info,
    329             EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf));
    330 }
    331 
    332 #if 0
    333 #include <errno.h>
    334 #include <stdio.h>
    335 
    336 typedef struct HeapDumpContext {
    337     FILE *fp;
    338     void *chunkStart;
    339     size_t chunkLen;
    340     bool chunkFree;
    341 } HeapDumpContext;
    342 
    343 static void
    344 dump_context(const HeapDumpContext *ctx)
    345 {
    346     fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart,
    347             ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED");
    348 }
    349 
    350 static void
    351 heap_chunk_callback(const void *chunkptr, size_t chunklen,
    352                     const void *userptr, size_t userlen, void *arg)
    353 {
    354     HeapDumpContext *ctx = (HeapDumpContext *)arg;
    355     bool chunkFree = (userptr == NULL);
    356 
    357     if (chunkFree != ctx->chunkFree ||
    358             ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr)
    359     {
    360         /* The new chunk is of a different type or isn't
    361          * contiguous with the current chunk.  Dump the
    362          * old one and start a new one.
    363          */
    364         if (ctx->chunkStart != NULL) {
    365             /* It's not the first chunk. */
    366             dump_context(ctx);
    367         }
    368         ctx->chunkStart = (void *)chunkptr;
    369         ctx->chunkLen = chunklen;
    370         ctx->chunkFree = chunkFree;
    371     } else {
    372         /* Extend the current chunk.
    373          */
    374         ctx->chunkLen += chunklen;
    375     }
    376 }
    377 
    378 /* Dumps free and used ranges, as text, to the named file.
    379  */
    380 void dvmDumpHeapToFile(const char *fileName)
    381 {
    382     HeapDumpContext ctx;
    383     FILE *fp;
    384 
    385     fp = fopen(fileName, "w+");
    386     if (fp == NULL) {
    387         LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno));
    388         return;
    389     }
    390     LOGW("Dumping heap to %s...\n", fileName);
    391 
    392     fprintf(fp, "==== Dalvik heap dump ====\n");
    393     memset(&ctx, 0, sizeof(ctx));
    394     ctx.fp = fp;
    395     dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
    396     dump_context(&ctx);
    397     fprintf(fp, "==== end heap dump ====\n");
    398 
    399     LOGW("Dumped heap to %s.\n", fileName);
    400 
    401     fclose(fp);
    402 }
    403 #endif
    404