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 <malloc.h>
19
20#include "Dalvik.h"
21#include "HeapInternal.h"
22#include "HeapSource.h"
23#include "Float12.h"
24
25int 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
42static 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
111void 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
293void 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
336typedef struct HeapDumpContext {
337    FILE *fp;
338    void *chunkStart;
339    size_t chunkLen;
340    bool chunkFree;
341} HeapDumpContext;
342
343static void
344dump_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
350static void
351heap_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 */
380void 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