blob: 4a877d2de59f6f7499adab71ffd69ec940c67f3b [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>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
31#ifdef HAVE_MALLOC_H
32#include <malloc.h>
33#endif
34
35namespace android
36{
37
38static jfieldID dalvikPss_field;
39static jfieldID dalvikPrivateDirty_field;
40static jfieldID dalvikSharedDirty_field;
41static jfieldID nativePss_field;
42static jfieldID nativePrivateDirty_field;
43static jfieldID nativeSharedDirty_field;
44static jfieldID otherPss_field;
45static jfieldID otherPrivateDirty_field;
46static jfieldID otherSharedDirty_field;
47
48struct stats_t {
49 int dalvikPss;
50 int dalvikPrivateDirty;
51 int dalvikSharedDirty;
52
53 int nativePss;
54 int nativePrivateDirty;
55 int nativeSharedDirty;
56
57 int otherPss;
58 int otherPrivateDirty;
59 int otherSharedDirty;
60};
61
62#define BINDER_STATS "/proc/binder/stats"
63
64static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
65{
66#ifdef HAVE_MALLOC_H
67 struct mallinfo info = mallinfo();
68 return (jlong) info.usmblks;
69#else
70 return -1;
71#endif
72}
73
74static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
75{
76#ifdef HAVE_MALLOC_H
77 struct mallinfo info = mallinfo();
78 return (jlong) info.uordblks;
79#else
80 return -1;
81#endif
82}
83
84static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
85{
86#ifdef HAVE_MALLOC_H
87 struct mallinfo info = mallinfo();
88 return (jlong) info.fordblks;
89#else
90 return -1;
91#endif
92}
93
94static void read_mapinfo(FILE *fp, stats_t* stats)
95{
96 char line[1024];
97 int len;
98 bool skip, done = false;
99
100 unsigned start = 0, size = 0, resident = 0, pss = 0;
101 unsigned shared_clean = 0, shared_dirty = 0;
102 unsigned private_clean = 0, private_dirty = 0;
103 unsigned referenced = 0;
104 unsigned temp;
105
106 int isNativeHeap;
107 int isDalvikHeap;
108 int isSqliteHeap;
109
110 if(fgets(line, 1024, fp) == 0) return;
111
112 while (!done) {
113 isNativeHeap = 0;
114 isDalvikHeap = 0;
115 isSqliteHeap = 0;
116 skip = false;
117
118 len = strlen(line);
119 if (len < 1) return;
120 line[--len] = 0;
121
122 /* ignore guard pages */
123 if (len > 18 && line[17] == '-') skip = true;
124
125 start = strtoul(line, 0, 16);
126
127 if (strstr(line, "[heap]")) {
128 isNativeHeap = 1;
129 } else if (strstr(line, "/dalvik-LinearAlloc")) {
130 isDalvikHeap = 1;
131 } else if (strstr(line, "/mspace/dalvik-heap")) {
132 isDalvikHeap = 1;
133 } else if (strstr(line, "/dalvik-heap-bitmap/")) {
134 isDalvikHeap = 1;
Grace Klobabd511162009-07-08 23:32:25 -0700135 } else if (strstr(line, "/data/dalvik-cache/")) {
136 isDalvikHeap = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 } else if (strstr(line, "/tmp/sqlite-heap")) {
138 isSqliteHeap = 1;
139 }
140
141 //LOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
142 // isSqliteHeap, line);
143
144 while (true) {
145 if (fgets(line, 1024, fp) == 0) {
146 done = true;
147 break;
148 }
149
150 if (sscanf(line, "Size: %d kB", &temp) == 1) {
151 size = temp;
152 } else if (sscanf(line, "Rss: %d kB", &temp) == 1) {
153 resident = temp;
154 } else if (sscanf(line, "Pss: %d kB", &temp) == 1) {
155 pss = temp;
156 } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
157 shared_clean = temp;
158 } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
159 shared_dirty = temp;
160 } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
161 private_clean = temp;
162 } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
163 private_dirty = temp;
164 } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) {
165 referenced = temp;
Grace Klobabd511162009-07-08 23:32:25 -0700166 } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 // looks like a new mapping
Grace Klobabd511162009-07-08 23:32:25 -0700168 // example: "10000000-10001000 ---p 10000000 00:00 0"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 break;
170 }
171 }
172
173 if (!skip) {
174 if (isNativeHeap) {
175 stats->nativePss += pss;
176 stats->nativePrivateDirty += private_dirty;
177 stats->nativeSharedDirty += shared_dirty;
178 } else if (isDalvikHeap) {
179 stats->dalvikPss += pss;
180 stats->dalvikPrivateDirty += private_dirty;
181 stats->dalvikSharedDirty += shared_dirty;
182 } else if ( isSqliteHeap) {
183 // ignore
184 } else {
185 stats->otherPss += pss;
Grace Klobabd511162009-07-08 23:32:25 -0700186 stats->otherPrivateDirty += private_dirty;
187 stats->otherSharedDirty += shared_dirty;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 }
189 }
190 }
191}
192
193static void load_maps(int pid, stats_t* stats)
194{
195 char tmp[128];
196 FILE *fp;
197
198 sprintf(tmp, "/proc/%d/smaps", pid);
199 fp = fopen(tmp, "r");
200 if (fp == 0) return;
201
202 read_mapinfo(fp, stats);
203 fclose(fp);
204}
205
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700206static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
207 jint pid, jobject object)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208{
209 stats_t stats;
210 memset(&stats, 0, sizeof(stats_t));
211
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700212 load_maps(pid, &stats);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213
214 env->SetIntField(object, dalvikPss_field, stats.dalvikPss);
215 env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty);
216 env->SetIntField(object, dalvikSharedDirty_field, stats.dalvikSharedDirty);
217
218 env->SetIntField(object, nativePss_field, stats.nativePss);
219 env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty);
220 env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty);
221
222 env->SetIntField(object, otherPss_field, stats.otherPss);
223 env->SetIntField(object, otherPrivateDirty_field, stats.otherPrivateDirty);
224 env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty);
225}
226
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700227static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
228{
229 android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
230}
231
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232static jint read_binder_stat(const char* stat)
233{
234 FILE* fp = fopen(BINDER_STATS, "r");
235 if (fp == NULL) {
236 return -1;
237 }
238
239 char line[1024];
240
241 char compare[128];
242 int len = snprintf(compare, 128, "proc %d", getpid());
243
244 // loop until we have the block that represents this process
245 do {
246 if (fgets(line, 1024, fp) == 0) {
247 return -1;
248 }
249 } while (strncmp(compare, line, len));
250
251 // now that we have this process, read until we find the stat that we are looking for
252 len = snprintf(compare, 128, " %s: ", stat);
253
254 do {
255 if (fgets(line, 1024, fp) == 0) {
256 return -1;
257 }
258 } while (strncmp(compare, line, len));
259
260 // we have the line, now increment the line ptr to the value
261 char* ptr = line + len;
262 return atoi(ptr);
263}
264
265static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
266{
267 return read_binder_stat("bcTRANSACTION");
268}
269
270static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
271{
272 return read_binder_stat("brTRANSACTION");
273}
274
275// these are implemented in android_util_Binder.cpp
276jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
277jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
278jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
279
Andy McFadden06a6b552010-07-13 16:28:09 -0700280
281#ifdef HAVE_ANDROID_OS
282/* pulled out of bionic */
283extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
284 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
285extern "C" void free_malloc_leak_info(uint8_t* info);
286#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
287#define BACKTRACE_SIZE 32
288
289/*
290 * This is a qsort() callback.
291 *
292 * See dumpNativeHeap() for comments about the data format and sort order.
293 */
294static int compareHeapRecords(const void* vrec1, const void* vrec2)
295{
296 const size_t* rec1 = (const size_t*) vrec1;
297 const size_t* rec2 = (const size_t*) vrec2;
298 size_t size1 = *rec1;
299 size_t size2 = *rec2;
300
301 if (size1 < size2) {
302 return 1;
303 } else if (size1 > size2) {
304 return -1;
305 }
306
307 intptr_t* bt1 = (intptr_t*)(rec1 + 2);
308 intptr_t* bt2 = (intptr_t*)(rec2 + 2);
309 for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
310 intptr_t addr1 = bt1[idx];
311 intptr_t addr2 = bt2[idx];
312 if (addr1 == addr2) {
313 if (addr1 == 0)
314 break;
315 continue;
316 }
317 if (addr1 < addr2) {
318 return -1;
319 } else if (addr1 > addr2) {
320 return 1;
321 }
322 }
323
324 return 0;
325}
326
327/*
328 * The get_malloc_leak_info() call returns an array of structs that
329 * look like this:
330 *
331 * size_t size
332 * size_t allocations
333 * intptr_t backtrace[32]
334 *
335 * "size" is the size of the allocation, "backtrace" is a fixed-size
336 * array of function pointers, and "allocations" is the number of
337 * allocations with the exact same size and backtrace.
338 *
339 * The entries are sorted by descending total size (i.e. size*allocations)
340 * then allocation count. For best results with "diff" we'd like to sort
341 * primarily by individual size then stack trace. Since the entries are
342 * fixed-size, and we're allowed (by the current implementation) to mangle
343 * them, we can do this in place.
344 */
345static void dumpNativeHeap(FILE* fp)
346{
347 uint8_t* info = NULL;
348 size_t overallSize, infoSize, totalMemory, backtraceSize;
349
350 get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
351 &backtraceSize);
352 if (info == NULL) {
353 fprintf(fp, "Native heap dump not available. To enable, run these"
354 " commands (requires root):\n");
355 fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
356 fprintf(fp, "$ adb shell stop\n");
357 fprintf(fp, "$ adb shell start\n");
358 return;
359 }
360 assert(infoSize != 0);
361 assert(overallSize % infoSize == 0);
362
363 fprintf(fp, "Android Native Heap Dump v1.0\n\n");
364
365 size_t recordCount = overallSize / infoSize;
366 fprintf(fp, "Total memory: %zu\n", totalMemory);
367 fprintf(fp, "Allocation records: %zd\n", recordCount);
368 if (backtraceSize != BACKTRACE_SIZE) {
369 fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
370 backtraceSize, BACKTRACE_SIZE);
371 }
372 fprintf(fp, "\n");
373
374 /* re-sort the entries */
375 qsort(info, recordCount, infoSize, compareHeapRecords);
376
377 /* dump the entries to the file */
378 const uint8_t* ptr = info;
379 for (size_t idx = 0; idx < recordCount; idx++) {
380 size_t size = *(size_t*) ptr;
381 size_t allocations = *(size_t*) (ptr + sizeof(size_t));
382 intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
383
384 fprintf(fp, "z %d sz %8zu num %4zu bt",
385 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
386 size & ~SIZE_FLAG_ZYGOTE_CHILD,
387 allocations);
388 for (size_t bt = 0; bt < backtraceSize; bt++) {
389 if (backtrace[bt] == 0) {
390 break;
391 } else {
392 fprintf(fp, " %08x", backtrace[bt]);
393 }
394 }
395 fprintf(fp, "\n");
396
397 ptr += infoSize;
398 }
399
400 fprintf(fp, "END\n");
401 free_malloc_leak_info(info);
402}
403#endif /*HAVE_ANDROID_OS*/
404
405/*
406 * Dump the native heap, writing human-readable output to the specified
407 * file descriptor.
408 */
409static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
410 jobject fileDescriptor)
411{
412 if (fileDescriptor == NULL) {
413 jniThrowNullPointerException(env, NULL);
414 return;
415 }
416 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
417 if (origFd < 0) {
418 jniThrowRuntimeException(env, "Invalid file descriptor");
419 return;
420 }
421
422 /* dup() the descriptor so we don't close the original with fclose() */
423 int fd = dup(origFd);
424 if (fd < 0) {
425 LOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
426 jniThrowRuntimeException(env, "dup() failed");
427 return;
428 }
429
430 FILE* fp = fdopen(fd, "w");
431 if (fp == NULL) {
432 LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
433 close(fd);
434 jniThrowRuntimeException(env, "fdopen() failed");
435 return;
436 }
437
438#ifdef HAVE_ANDROID_OS
439 LOGD("Native heap dump starting...\n");
440 dumpNativeHeap(fp);
441 LOGD("Native heap dump complete.\n");
442#else
443 fprintf(fp, "Native heap dump not available on this platform\n");
444#endif
445
446 fclose(fp);
447}
448
449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450/*
451 * JNI registration.
452 */
453
454static JNINativeMethod gMethods[] = {
455 { "getNativeHeapSize", "()J",
456 (void*) android_os_Debug_getNativeHeapSize },
457 { "getNativeHeapAllocatedSize", "()J",
458 (void*) android_os_Debug_getNativeHeapAllocatedSize },
459 { "getNativeHeapFreeSize", "()J",
460 (void*) android_os_Debug_getNativeHeapFreeSize },
461 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
462 (void*) android_os_Debug_getDirtyPages },
Dianne Hackborn3025ef32009-08-31 21:31:47 -0700463 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V",
464 (void*) android_os_Debug_getDirtyPagesPid },
Andy McFadden06a6b552010-07-13 16:28:09 -0700465 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V",
466 (void*) android_os_Debug_dumpNativeHeap },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 { "getBinderSentTransactions", "()I",
468 (void*) android_os_Debug_getBinderSentTransactions },
469 { "getBinderReceivedTransactions", "()I",
470 (void*) android_os_getBinderReceivedTransactions },
471 { "getBinderLocalObjectCount", "()I",
472 (void*)android_os_Debug_getLocalObjectCount },
473 { "getBinderProxyObjectCount", "()I",
474 (void*)android_os_Debug_getProxyObjectCount },
475 { "getBinderDeathObjectCount", "()I",
476 (void*)android_os_Debug_getDeathObjectCount },
477};
478
479int register_android_os_Debug(JNIEnv *env)
480{
481 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
482
483 dalvikPss_field = env->GetFieldID(clazz, "dalvikPss", "I");
484 dalvikPrivateDirty_field = env->GetFieldID(clazz, "dalvikPrivateDirty", "I");
485 dalvikSharedDirty_field = env->GetFieldID(clazz, "dalvikSharedDirty", "I");
486
487 nativePss_field = env->GetFieldID(clazz, "nativePss", "I");
488 nativePrivateDirty_field = env->GetFieldID(clazz, "nativePrivateDirty", "I");
489 nativeSharedDirty_field = env->GetFieldID(clazz, "nativeSharedDirty", "I");
490
491 otherPss_field = env->GetFieldID(clazz, "otherPss", "I");
492 otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I");
493 otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I");
494
495 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
496}
497
Andy McFadden06a6b552010-07-13 16:28:09 -0700498}; // namespace android