blob: 3a3f07e0dacf3fd4c0b69c1ac79da368397dc549 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
Andy McFadden06a6b552010-07-13 16:28:09 -070017#define LOG_TAG "android.os.Debug"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018#include "JNIHelp.h"
19#include "jni.h"
20#include "utils/misc.h"
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <time.h>
27#include <sys/time.h>
Andy McFadden06a6b552010-07-13 16:28:09 -070028#include <errno.h>
29#include <assert.h>
Dianne Hackborn0e3328f2011-07-17 13:31:17 -070030#include <ctype.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
32#ifdef HAVE_MALLOC_H
33#include <malloc.h>
34#endif
35
36namespace android
37{
38
Dianne Hackborn0e3328f2011-07-17 13:31:17 -070039enum {
40 HEAP_UNKNOWN,
41 HEAP_DALVIK,
42 HEAP_NATIVE,
43 HEAP_CURSOR,
44 HEAP_ASHMEM,
45 HEAP_UNKNOWN_DEV,
46 HEAP_SO,
47 HEAP_JAR,
48 HEAP_APK,
49 HEAP_TTF,
50 HEAP_DEX,
51 HEAP_UNKNOWN_MAP,
52
53 _NUM_HEAP,
54 _NUM_CORE_HEAP = HEAP_NATIVE+1
55};
56
57struct stat_fields {
58 jfieldID pss_field;
59 jfieldID privateDirty_field;
60 jfieldID sharedDirty_field;
61};
62
63struct stat_field_names {
64 const char* pss_name;
65 const char* privateDirty_name;
66 const char* sharedDirty_name;
67};
68
69static stat_fields stat_fields[_NUM_CORE_HEAP];
70
71static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
72 { "otherPss", "otherPrivateDirty", "otherSharedDirty" },
73 { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" },
74 { "nativePss", "nativePrivateDirty", "nativeSharedDirty" }
75};
76
77jfieldID otherStats_field;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
79struct stats_t {
Dianne Hackborn0e3328f2011-07-17 13:31:17 -070080 int pss;
81 int privateDirty;
82 int sharedDirty;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083};
84
85#define BINDER_STATS "/proc/binder/stats"
86
87static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
88{
89#ifdef HAVE_MALLOC_H
90 struct mallinfo info = mallinfo();
91 return (jlong) info.usmblks;
92#else
93 return -1;
94#endif
95}
96
97static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
98{
99#ifdef HAVE_MALLOC_H
100 struct mallinfo info = mallinfo();
101 return (jlong) info.uordblks;
102#else
103 return -1;
104#endif
105}
106
107static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
108{
109#ifdef HAVE_MALLOC_H
110 struct mallinfo info = mallinfo();
111 return (jlong) info.fordblks;
112#else
113 return -1;
114#endif
115}
116
117static void read_mapinfo(FILE *fp, stats_t* stats)
118{
119 char line[1024];
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700120 int len, nameLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 bool skip, done = false;
122
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700123 unsigned size = 0, resident = 0, pss = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 unsigned shared_clean = 0, shared_dirty = 0;
125 unsigned private_clean = 0, private_dirty = 0;
126 unsigned referenced = 0;
127 unsigned temp;
128
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700129 unsigned long int start;
130 unsigned long int end = 0;
131 unsigned long int prevEnd = 0;
132 char* name;
133 int name_pos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700135 int whichHeap = HEAP_UNKNOWN;
136 int prevHeap = HEAP_UNKNOWN;
137
138 if(fgets(line, sizeof(line), fp) == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
140 while (!done) {
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700141 prevHeap = whichHeap;
142 prevEnd = end;
143 whichHeap = HEAP_UNKNOWN;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 skip = false;
145
146 len = strlen(line);
147 if (len < 1) return;
148 line[--len] = 0;
149
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700150 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
151 skip = true;
152 } else {
153 while (isspace(line[name_pos])) {
154 name_pos += 1;
155 }
156 name = line + name_pos;
157 nameLen = strlen(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700159 if (strstr(name, "[heap]") == name) {
160 whichHeap = HEAP_NATIVE;
161 } else if (strstr(name, "/dev/ashmem/dalvik-") == name) {
162 whichHeap = HEAP_DALVIK;
163 } else if (strstr(name, "/dev/ashmem/CursorWindow") == name) {
164 whichHeap = HEAP_CURSOR;
165 } else if (strstr(name, "/dev/ashmem/") == name) {
166 whichHeap = HEAP_ASHMEM;
167 } else if (strstr(name, "/dev/") == name) {
168 whichHeap = HEAP_UNKNOWN_DEV;
169 } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
170 whichHeap = HEAP_SO;
171 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
172 whichHeap = HEAP_JAR;
173 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
174 whichHeap = HEAP_APK;
175 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
176 whichHeap = HEAP_TTF;
177 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) {
178 whichHeap = HEAP_DEX;
179 } else if (nameLen > 0) {
180 whichHeap = HEAP_UNKNOWN_MAP;
181 } else if (start == prevEnd && prevHeap == HEAP_SO) {
182 // bss section of a shared library.
183 whichHeap = HEAP_SO;
184 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 }
186
187 //LOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
188 // isSqliteHeap, line);
189
190 while (true) {
191 if (fgets(line, 1024, fp) == 0) {
192 done = true;
193 break;
194 }
195
196 if (sscanf(line, "Size: %d kB", &temp) == 1) {
197 size = temp;
198 } else if (sscanf(line, "Rss: %d kB", &temp) == 1) {
199 resident = temp;
200 } else if (sscanf(line, "Pss: %d kB", &temp) == 1) {
201 pss = temp;
202 } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
203 shared_clean = temp;
204 } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
205 shared_dirty = temp;
206 } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
207 private_clean = temp;
208 } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
209 private_dirty = temp;
210 } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) {
211 referenced = temp;
Grace Klobabd511162009-07-08 23:32:25 -0700212 } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 // looks like a new mapping
Grace Klobabd511162009-07-08 23:32:25 -0700214 // example: "10000000-10001000 ---p 10000000 00:00 0"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 break;
216 }
217 }
218
219 if (!skip) {
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700220 stats[whichHeap].pss += pss;
221 stats[whichHeap].privateDirty += private_dirty;
222 stats[whichHeap].sharedDirty += shared_dirty;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
224 }
225}
226
227static void load_maps(int pid, stats_t* stats)
228{
229 char tmp[128];
230 FILE *fp;
231
232 sprintf(tmp, "/proc/%d/smaps", pid);
233 fp = fopen(tmp, "r");
234 if (fp == 0) return;
235
236 read_mapinfo(fp, stats);
237 fclose(fp);
238}
239
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700240static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
241 jint pid, jobject object)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242{
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700243 stats_t stats[_NUM_HEAP];
244 memset(&stats, 0, sizeof(stats));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700246 load_maps(pid, stats);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700248 for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
249 stats[HEAP_UNKNOWN].pss += stats[i].pss;
250 stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
251 stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
252 }
253
254 for (int i=0; i<_NUM_CORE_HEAP; i++) {
255 env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
256 env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
257 env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
258 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700260 jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700262 jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
263 if (otherArray == NULL) {
264 return;
265 }
266
267 int j=0;
268 for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
269 otherArray[j++] = stats[i].pss;
270 otherArray[j++] = stats[i].privateDirty;
271 otherArray[j++] = stats[i].sharedDirty;
272 }
273
274 env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275}
276
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700277static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
278{
279 android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
280}
281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282static jint read_binder_stat(const char* stat)
283{
284 FILE* fp = fopen(BINDER_STATS, "r");
285 if (fp == NULL) {
286 return -1;
287 }
288
289 char line[1024];
290
291 char compare[128];
292 int len = snprintf(compare, 128, "proc %d", getpid());
293
294 // loop until we have the block that represents this process
295 do {
296 if (fgets(line, 1024, fp) == 0) {
297 return -1;
298 }
299 } while (strncmp(compare, line, len));
300
301 // now that we have this process, read until we find the stat that we are looking for
302 len = snprintf(compare, 128, " %s: ", stat);
303
304 do {
305 if (fgets(line, 1024, fp) == 0) {
306 return -1;
307 }
308 } while (strncmp(compare, line, len));
309
310 // we have the line, now increment the line ptr to the value
311 char* ptr = line + len;
312 return atoi(ptr);
313}
314
315static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
316{
317 return read_binder_stat("bcTRANSACTION");
318}
319
320static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
321{
322 return read_binder_stat("brTRANSACTION");
323}
324
325// these are implemented in android_util_Binder.cpp
326jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
327jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
328jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
329
Andy McFadden06a6b552010-07-13 16:28:09 -0700330
Andy McFadden06a6b552010-07-13 16:28:09 -0700331/* pulled out of bionic */
332extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
333 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
334extern "C" void free_malloc_leak_info(uint8_t* info);
335#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
336#define BACKTRACE_SIZE 32
337
338/*
339 * This is a qsort() callback.
340 *
341 * See dumpNativeHeap() for comments about the data format and sort order.
342 */
343static int compareHeapRecords(const void* vrec1, const void* vrec2)
344{
345 const size_t* rec1 = (const size_t*) vrec1;
346 const size_t* rec2 = (const size_t*) vrec2;
347 size_t size1 = *rec1;
348 size_t size2 = *rec2;
349
350 if (size1 < size2) {
351 return 1;
352 } else if (size1 > size2) {
353 return -1;
354 }
355
356 intptr_t* bt1 = (intptr_t*)(rec1 + 2);
357 intptr_t* bt2 = (intptr_t*)(rec2 + 2);
358 for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
359 intptr_t addr1 = bt1[idx];
360 intptr_t addr2 = bt2[idx];
361 if (addr1 == addr2) {
362 if (addr1 == 0)
363 break;
364 continue;
365 }
366 if (addr1 < addr2) {
367 return -1;
368 } else if (addr1 > addr2) {
369 return 1;
370 }
371 }
372
373 return 0;
374}
375
376/*
377 * The get_malloc_leak_info() call returns an array of structs that
378 * look like this:
379 *
380 * size_t size
381 * size_t allocations
382 * intptr_t backtrace[32]
383 *
384 * "size" is the size of the allocation, "backtrace" is a fixed-size
385 * array of function pointers, and "allocations" is the number of
386 * allocations with the exact same size and backtrace.
387 *
388 * The entries are sorted by descending total size (i.e. size*allocations)
389 * then allocation count. For best results with "diff" we'd like to sort
390 * primarily by individual size then stack trace. Since the entries are
391 * fixed-size, and we're allowed (by the current implementation) to mangle
392 * them, we can do this in place.
393 */
394static void dumpNativeHeap(FILE* fp)
395{
396 uint8_t* info = NULL;
397 size_t overallSize, infoSize, totalMemory, backtraceSize;
398
399 get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
400 &backtraceSize);
401 if (info == NULL) {
402 fprintf(fp, "Native heap dump not available. To enable, run these"
403 " commands (requires root):\n");
404 fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
405 fprintf(fp, "$ adb shell stop\n");
406 fprintf(fp, "$ adb shell start\n");
407 return;
408 }
409 assert(infoSize != 0);
410 assert(overallSize % infoSize == 0);
411
412 fprintf(fp, "Android Native Heap Dump v1.0\n\n");
413
414 size_t recordCount = overallSize / infoSize;
415 fprintf(fp, "Total memory: %zu\n", totalMemory);
416 fprintf(fp, "Allocation records: %zd\n", recordCount);
417 if (backtraceSize != BACKTRACE_SIZE) {
418 fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
419 backtraceSize, BACKTRACE_SIZE);
420 }
421 fprintf(fp, "\n");
422
423 /* re-sort the entries */
424 qsort(info, recordCount, infoSize, compareHeapRecords);
425
426 /* dump the entries to the file */
427 const uint8_t* ptr = info;
428 for (size_t idx = 0; idx < recordCount; idx++) {
429 size_t size = *(size_t*) ptr;
430 size_t allocations = *(size_t*) (ptr + sizeof(size_t));
431 intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
432
433 fprintf(fp, "z %d sz %8zu num %4zu bt",
434 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
435 size & ~SIZE_FLAG_ZYGOTE_CHILD,
436 allocations);
437 for (size_t bt = 0; bt < backtraceSize; bt++) {
438 if (backtrace[bt] == 0) {
439 break;
440 } else {
441 fprintf(fp, " %08x", backtrace[bt]);
442 }
443 }
444 fprintf(fp, "\n");
445
446 ptr += infoSize;
447 }
448
Andy McFadden06a6b552010-07-13 16:28:09 -0700449 free_malloc_leak_info(info);
Brian Carlstrom393b84c12010-10-17 23:40:43 -0700450
451 fprintf(fp, "MAPS\n");
452 const char* maps = "/proc/self/maps";
453 FILE* in = fopen(maps, "r");
454 if (in == NULL) {
455 fprintf(fp, "Could not open %s\n", maps);
456 return;
457 }
458 char buf[BUFSIZ];
459 while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
460 fwrite(buf, sizeof(char), n, fp);
461 }
462 fclose(in);
463
464 fprintf(fp, "END\n");
Andy McFadden06a6b552010-07-13 16:28:09 -0700465}
Andy McFadden06a6b552010-07-13 16:28:09 -0700466
467/*
468 * Dump the native heap, writing human-readable output to the specified
469 * file descriptor.
470 */
471static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
472 jobject fileDescriptor)
473{
474 if (fileDescriptor == NULL) {
475 jniThrowNullPointerException(env, NULL);
476 return;
477 }
478 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
479 if (origFd < 0) {
480 jniThrowRuntimeException(env, "Invalid file descriptor");
481 return;
482 }
483
484 /* dup() the descriptor so we don't close the original with fclose() */
485 int fd = dup(origFd);
486 if (fd < 0) {
487 LOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
488 jniThrowRuntimeException(env, "dup() failed");
489 return;
490 }
491
492 FILE* fp = fdopen(fd, "w");
493 if (fp == NULL) {
494 LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
495 close(fd);
496 jniThrowRuntimeException(env, "fdopen() failed");
497 return;
498 }
499
Andy McFadden06a6b552010-07-13 16:28:09 -0700500 LOGD("Native heap dump starting...\n");
501 dumpNativeHeap(fp);
502 LOGD("Native heap dump complete.\n");
Andy McFadden06a6b552010-07-13 16:28:09 -0700503
504 fclose(fp);
505}
506
507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508/*
509 * JNI registration.
510 */
511
512static JNINativeMethod gMethods[] = {
513 { "getNativeHeapSize", "()J",
514 (void*) android_os_Debug_getNativeHeapSize },
515 { "getNativeHeapAllocatedSize", "()J",
516 (void*) android_os_Debug_getNativeHeapAllocatedSize },
517 { "getNativeHeapFreeSize", "()J",
518 (void*) android_os_Debug_getNativeHeapFreeSize },
519 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
520 (void*) android_os_Debug_getDirtyPages },
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700521 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V",
522 (void*) android_os_Debug_getDirtyPagesPid },
Andy McFadden06a6b552010-07-13 16:28:09 -0700523 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V",
524 (void*) android_os_Debug_dumpNativeHeap },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 { "getBinderSentTransactions", "()I",
526 (void*) android_os_Debug_getBinderSentTransactions },
527 { "getBinderReceivedTransactions", "()I",
528 (void*) android_os_getBinderReceivedTransactions },
529 { "getBinderLocalObjectCount", "()I",
530 (void*)android_os_Debug_getLocalObjectCount },
531 { "getBinderProxyObjectCount", "()I",
532 (void*)android_os_Debug_getProxyObjectCount },
533 { "getBinderDeathObjectCount", "()I",
534 (void*)android_os_Debug_getDeathObjectCount },
535};
536
537int register_android_os_Debug(JNIEnv *env)
538{
539 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540
Dianne Hackborn0e3328f2011-07-17 13:31:17 -0700541 for (int i=0; i<_NUM_CORE_HEAP; i++) {
542 stat_fields[i].pss_field =
543 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
544 stat_fields[i].privateDirty_field =
545 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
546 stat_fields[i].sharedDirty_field =
547 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
548 }
549
550 otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
553}
554
Andy McFadden06a6b552010-07-13 16:28:09 -0700555}; // namespace android