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
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 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
dvmLogMadviseStats(size_t madvisedSizes[],size_t arrayLen)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