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
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    size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT],
114           perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT],
115           perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT],
116           perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT];
117    unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4];
118    size_t actualSize, allowedSize, numAllocated, sizeAllocated;
119    size_t softLimit = dvmHeapSourceGetIdealFootprint();
120    size_t nHeaps = dvmHeapSourceGetNumHeaps();
121
122    /* Enough to quiet down gcc for unitialized variable check */
123    perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] =
124                           perHeapSizeAllocated[0] = 0;
125    actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize,
126                                       HEAP_SOURCE_MAX_HEAP_COUNT);
127    allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT,
128                      perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT);
129    numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED,
130                      perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
131    sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED,
132                      perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
133
134    /*
135     * Construct the the first 64-bit value to write to the log.
136     * Global information:
137     *
138     * [63   ] Must be zero
139     * [62-24] ASCII process identifier
140     * [23-12] GC time in ms
141     * [11- 0] Bytes freed
142     *
143     */
144    long long event0;
145    event0 = 0LL << 63 |
146            (long long)intToFloat12(gcTimeMs) << 12 |
147            (long long)intToFloat12(sizeFreed);
148    insertProcessName(&event0);
149
150    /*
151     * Aggregated heap stats:
152     *
153     * [63-62] 10
154     * [61-60] Reserved; must be zero
155     * [59-48] Objects freed
156     * [47-36] Actual size (current footprint)
157     * [35-24] Allowed size (current hard max)
158     * [23-12] Objects allocated
159     * [11- 0] Bytes allocated
160     */
161    long long event1;
162    event1 = 2LL << 62 |
163            (long long)intToFloat12(numFreed) << 48 |
164            (long long)intToFloat12(actualSize) << 36 |
165            (long long)intToFloat12(allowedSize) << 24 |
166            (long long)intToFloat12(numAllocated) << 12 |
167            (long long)intToFloat12(sizeAllocated);
168
169    /*
170     * Report the current state of the zygote heap(s).
171     *
172     * The active heap is always heap[0].  We can be in one of three states
173     * at present:
174     *
175     *  (1) Still in the zygote.  Zygote using heap[0].
176     *  (2) In the zygote, when the first child is started.  We created a
177     *      new heap just before the first fork() call, so the original
178     *      "zygote heap" is now heap[1], and we have a small heap[0] for
179     *      anything we do from here on.
180     *  (3) In an app process.  The app gets a new heap[0], and can also
181     *      see the two zygote heaps [1] and [2] (probably unwise to
182     *      assume any specific ordering).
183     *
184     * So if nHeaps == 1, we want the stats from heap[0]; else we want
185     * the sum of the values from heap[1] to heap[nHeaps-1].
186     *
187     *
188     * Zygote heap stats (except for the soft limit, which belongs to the
189     * active heap):
190     *
191     * [63-62] 11
192     * [61-60] Reserved; must be zero
193     * [59-48] Soft Limit (for the active heap)
194     * [47-36] Actual size (current footprint)
195     * [35-24] Allowed size (current hard max)
196     * [23-12] Objects allocated
197     * [11- 0] Bytes allocated
198     */
199    long long event2;
200    size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated;
201    int firstHeap = (nHeaps == 1) ? 0 : 1;
202    size_t hh;
203
204    zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0;
205    for (hh = firstHeap; hh < nHeaps; hh++) {
206        zActualSize += perHeapActualSize[hh];
207        zAllowedSize += perHeapAllowedSize[hh];
208        zNumAllocated += perHeapNumAllocated[hh];
209        zSizeAllocated += perHeapSizeAllocated[hh];
210    }
211    event2 = 3LL << 62 |
212            (long long)intToFloat12(softLimit) << 48 |
213            (long long)intToFloat12(zActualSize) << 36 |
214            (long long)intToFloat12(zAllowedSize) << 24 |
215            (long long)intToFloat12(zNumAllocated) << 12 |
216            (long long)intToFloat12(zSizeAllocated);
217
218    /*
219     * Report the current external allocation stats and the native heap
220     * summary.
221     *
222     * [63-48] Reserved; must be zero (TODO: put new data in these slots)
223     * [47-36] dlmalloc_footprint
224     * [35-24] mallinfo: total allocated space
225     * [23-12] External byte limit
226     * [11- 0] External bytes allocated
227     */
228    long long event3;
229    size_t externalLimit, externalBytesAllocated;
230    size_t uordblks, footprint;
231
232#if 0
233    /*
234     * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost
235     * of a GC, so it's not horribly expensive but it's not free either.
236     */
237    extern size_t dlmalloc_footprint(void);
238    struct mallinfo mi;
239    //u8 start, end;
240
241    //start = dvmGetRelativeTimeNsec();
242    mi = mallinfo();
243    uordblks = mi.uordblks;
244    footprint = dlmalloc_footprint();
245    //end = dvmGetRelativeTimeNsec();
246    //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n",
247    //    (int)((end - start) / 1000), mi.uordblks, footprint);
248#else
249    uordblks = footprint = 0;
250#endif
251
252    externalLimit =
253            dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
254    externalBytesAllocated =
255            dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
256    event3 =
257            (long long)intToFloat12(footprint) << 36 |
258            (long long)intToFloat12(uordblks) << 24 |
259            (long long)intToFloat12(externalLimit) << 12 |
260            (long long)intToFloat12(externalBytesAllocated);
261
262    /* Build the event data.
263     * [ 0: 0] item count (4)
264     * [ 1: 1] EVENT_TYPE_LONG
265     * [ 2: 9] event0
266     * [10:10] EVENT_TYPE_LONG
267     * [11:18] event1
268     * [19:19] EVENT_TYPE_LONG
269     * [20:27] event2
270     * [28:28] EVENT_TYPE_LONG
271     * [29:36] event2
272     */
273    unsigned char *c = eventBuf;
274    *c++ = 4;
275    *c++ = EVENT_TYPE_LONG;
276    memcpy(c, &event0, sizeof(event0));
277    c += sizeof(event0);
278    *c++ = EVENT_TYPE_LONG;
279    memcpy(c, &event1, sizeof(event1));
280    c += sizeof(event1);
281    *c++ = EVENT_TYPE_LONG;
282    memcpy(c, &event2, sizeof(event2));
283    c += sizeof(event2);
284    *c++ = EVENT_TYPE_LONG;
285    memcpy(c, &event3, sizeof(event3));
286
287    (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST,
288            eventBuf, sizeof(eventBuf));
289}
290
291void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen)
292{
293    unsigned char eventBuf[1 + (1 + sizeof(int)) * 2];
294    size_t total, zyg;
295    size_t firstHeap, i;
296    size_t nHeaps = dvmHeapSourceGetNumHeaps();
297
298    assert(arrayLen >= nHeaps);
299
300    firstHeap = nHeaps > 1 ? 1 : 0;
301    total = 0;
302    zyg = 0;
303    for (i = 0; i < nHeaps; i++) {
304        total += madvisedSizes[i];
305        if (i >= firstHeap) {
306            zyg += madvisedSizes[i];
307        }
308    }
309
310    /* Build the event data.
311     * [ 0: 0] item count (2)
312     * [ 1: 1] EVENT_TYPE_INT
313     * [ 2: 5] total madvise byte count
314     * [ 6: 6] EVENT_TYPE_INT
315     * [ 7:10] zygote heap madvise byte count
316     */
317    unsigned char *c = eventBuf;
318    *c++ = 2;
319    *c++ = EVENT_TYPE_INT;
320    memcpy(c, &total, sizeof(total));
321    c += sizeof(total);
322    *c++ = EVENT_TYPE_INT;
323    memcpy(c, &zyg, sizeof(zyg));
324    c += sizeof(zyg);
325
326    (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info,
327            EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf));
328}
329
330#if 0
331#include <errno.h>
332#include <stdio.h>
333
334typedef struct HeapDumpContext {
335    FILE *fp;
336    void *chunkStart;
337    size_t chunkLen;
338    bool chunkFree;
339} HeapDumpContext;
340
341static void
342dump_context(const HeapDumpContext *ctx)
343{
344    fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart,
345            ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED");
346}
347
348static void
349heap_chunk_callback(const void *chunkptr, size_t chunklen,
350                    const void *userptr, size_t userlen, void *arg)
351{
352    HeapDumpContext *ctx = (HeapDumpContext *)arg;
353    bool chunkFree = (userptr == NULL);
354
355    if (chunkFree != ctx->chunkFree ||
356            ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr)
357    {
358        /* The new chunk is of a different type or isn't
359         * contiguous with the current chunk.  Dump the
360         * old one and start a new one.
361         */
362        if (ctx->chunkStart != NULL) {
363            /* It's not the first chunk. */
364            dump_context(ctx);
365        }
366        ctx->chunkStart = (void *)chunkptr;
367        ctx->chunkLen = chunklen;
368        ctx->chunkFree = chunkFree;
369    } else {
370        /* Extend the current chunk.
371         */
372        ctx->chunkLen += chunklen;
373    }
374}
375
376/* Dumps free and used ranges, as text, to the named file.
377 */
378void dvmDumpHeapToFile(const char *fileName)
379{
380    HeapDumpContext ctx;
381    FILE *fp;
382
383    fp = fopen(fileName, "w+");
384    if (fp == NULL) {
385        LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno));
386        return;
387    }
388    LOGW("Dumping heap to %s...\n", fileName);
389
390    fprintf(fp, "==== Dalvik heap dump ====\n");
391    memset(&ctx, 0, sizeof(ctx));
392    ctx.fp = fp;
393    dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
394    dump_context(&ctx);
395    fprintf(fp, "==== end heap dump ====\n");
396
397    LOGW("Dumped heap to %s.\n", fileName);
398
399    fclose(fp);
400}
401#endif
402