• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
dvmGetHeapDebugInfo(HeapDebugInfoType info)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
insertProcessName(long long * ep)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 
dvmLogGcStats(size_t numFreed,size_t sizeFreed,size_t gcTimeMs)111 void 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 
dvmLogMadviseStats(size_t madvisedSizes[],size_t arrayLen)291 void 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 
334 typedef struct HeapDumpContext {
335     FILE *fp;
336     void *chunkStart;
337     size_t chunkLen;
338     bool chunkFree;
339 } HeapDumpContext;
340 
341 static void
342 dump_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 
348 static void
349 heap_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  */
378 void 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