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