android_os_Debug.cpp revision afc10e174d73363185f42cb67ec429a64549f2c7
1/*
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
17#define LOG_TAG "android.os.Debug"
18#include "JNIHelp.h"
19#include "jni.h"
20#include <utils/String8.h>
21#include "utils/misc.h"
22#include "cutils/debugger.h"
23
24#include <fcntl.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <time.h>
30#include <sys/time.h>
31#include <errno.h>
32#include <assert.h>
33#include <ctype.h>
34
35#ifdef HAVE_MALLOC_H
36#include <malloc.h>
37#endif
38
39namespace android
40{
41
42enum {
43    HEAP_UNKNOWN,
44    HEAP_DALVIK,
45    HEAP_NATIVE,
46    HEAP_CURSOR,
47    HEAP_ASHMEM,
48    HEAP_UNKNOWN_DEV,
49    HEAP_SO,
50    HEAP_JAR,
51    HEAP_APK,
52    HEAP_TTF,
53    HEAP_DEX,
54    HEAP_UNKNOWN_MAP,
55
56    _NUM_HEAP,
57    _NUM_CORE_HEAP = HEAP_NATIVE+1
58};
59
60struct stat_fields {
61    jfieldID pss_field;
62    jfieldID privateDirty_field;
63    jfieldID sharedDirty_field;
64};
65
66struct stat_field_names {
67    const char* pss_name;
68    const char* privateDirty_name;
69    const char* sharedDirty_name;
70};
71
72static stat_fields stat_fields[_NUM_CORE_HEAP];
73
74static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
75    { "otherPss", "otherPrivateDirty", "otherSharedDirty" },
76    { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" },
77    { "nativePss", "nativePrivateDirty", "nativeSharedDirty" }
78};
79
80jfieldID otherStats_field;
81
82struct stats_t {
83    int pss;
84    int privateDirty;
85    int sharedDirty;
86};
87
88#define BINDER_STATS "/proc/binder/stats"
89
90static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
91{
92#ifdef HAVE_MALLOC_H
93    struct mallinfo info = mallinfo();
94    return (jlong) info.usmblks;
95#else
96    return -1;
97#endif
98}
99
100static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
101{
102#ifdef HAVE_MALLOC_H
103    struct mallinfo info = mallinfo();
104    return (jlong) info.uordblks;
105#else
106    return -1;
107#endif
108}
109
110static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
111{
112#ifdef HAVE_MALLOC_H
113    struct mallinfo info = mallinfo();
114    return (jlong) info.fordblks;
115#else
116    return -1;
117#endif
118}
119
120static void read_mapinfo(FILE *fp, stats_t* stats)
121{
122    char line[1024];
123    int len, nameLen;
124    bool skip, done = false;
125
126    unsigned size = 0, resident = 0, pss = 0;
127    unsigned shared_clean = 0, shared_dirty = 0;
128    unsigned private_clean = 0, private_dirty = 0;
129    unsigned referenced = 0;
130    unsigned temp;
131
132    unsigned long int start;
133    unsigned long int end = 0;
134    unsigned long int prevEnd = 0;
135    char* name;
136    int name_pos;
137
138    int whichHeap = HEAP_UNKNOWN;
139    int prevHeap = HEAP_UNKNOWN;
140
141    if(fgets(line, sizeof(line), fp) == 0) return;
142
143    while (!done) {
144        prevHeap = whichHeap;
145        prevEnd = end;
146        whichHeap = HEAP_UNKNOWN;
147        skip = false;
148
149        len = strlen(line);
150        if (len < 1) return;
151        line[--len] = 0;
152
153        if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
154            skip = true;
155        } else {
156            while (isspace(line[name_pos])) {
157                name_pos += 1;
158            }
159            name = line + name_pos;
160            nameLen = strlen(name);
161
162            if ((strstr(name, "[heap]") == name) ||
163                (strstr(name, "/dev/ashmem/libc malloc") == name)) {
164                whichHeap = HEAP_NATIVE;
165            } else if (strstr(name, "/dev/ashmem/dalvik-") == name) {
166                whichHeap = HEAP_DALVIK;
167            } else if (strstr(name, "/dev/ashmem/CursorWindow") == name) {
168                whichHeap = HEAP_CURSOR;
169            } else if (strstr(name, "/dev/ashmem/") == name) {
170                whichHeap = HEAP_ASHMEM;
171            } else if (strstr(name, "/dev/") == name) {
172                whichHeap = HEAP_UNKNOWN_DEV;
173            } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
174                whichHeap = HEAP_SO;
175            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
176                whichHeap = HEAP_JAR;
177            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
178                whichHeap = HEAP_APK;
179            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
180                whichHeap = HEAP_TTF;
181            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) {
182                whichHeap = HEAP_DEX;
183            } else if (nameLen > 0) {
184                whichHeap = HEAP_UNKNOWN_MAP;
185            } else if (start == prevEnd && prevHeap == HEAP_SO) {
186                // bss section of a shared library.
187                whichHeap = HEAP_SO;
188            }
189        }
190
191        //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
192        //    isSqliteHeap, line);
193
194        while (true) {
195            if (fgets(line, 1024, fp) == 0) {
196                done = true;
197                break;
198            }
199
200            if (sscanf(line, "Size: %d kB", &temp) == 1) {
201                size = temp;
202            } else if (sscanf(line, "Rss: %d kB", &temp) == 1) {
203                resident = temp;
204            } else if (sscanf(line, "Pss: %d kB", &temp) == 1) {
205                pss = temp;
206            } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
207                shared_clean = temp;
208            } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
209                shared_dirty = temp;
210            } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
211                private_clean = temp;
212            } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
213                private_dirty = temp;
214            } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) {
215                referenced = temp;
216            } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
217                // looks like a new mapping
218                // example: "10000000-10001000 ---p 10000000 00:00 0"
219                break;
220            }
221        }
222
223        if (!skip) {
224            stats[whichHeap].pss += pss;
225            stats[whichHeap].privateDirty += private_dirty;
226            stats[whichHeap].sharedDirty += shared_dirty;
227        }
228    }
229}
230
231static void load_maps(int pid, stats_t* stats)
232{
233    char tmp[128];
234    FILE *fp;
235
236    sprintf(tmp, "/proc/%d/smaps", pid);
237    fp = fopen(tmp, "r");
238    if (fp == 0) return;
239
240    read_mapinfo(fp, stats);
241    fclose(fp);
242}
243
244static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
245        jint pid, jobject object)
246{
247    stats_t stats[_NUM_HEAP];
248    memset(&stats, 0, sizeof(stats));
249
250    load_maps(pid, stats);
251
252    for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
253        stats[HEAP_UNKNOWN].pss += stats[i].pss;
254        stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
255        stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
256    }
257
258    for (int i=0; i<_NUM_CORE_HEAP; i++) {
259        env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
260        env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
261        env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
262    }
263
264    jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
265
266    jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
267    if (otherArray == NULL) {
268        return;
269    }
270
271    int j=0;
272    for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
273        otherArray[j++] = stats[i].pss;
274        otherArray[j++] = stats[i].privateDirty;
275        otherArray[j++] = stats[i].sharedDirty;
276    }
277
278    env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
279}
280
281static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
282{
283    android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
284}
285
286static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid)
287{
288    char line[1024];
289    jlong pss = 0;
290    unsigned temp;
291
292    char tmp[128];
293    FILE *fp;
294
295    sprintf(tmp, "/proc/%d/smaps", pid);
296    fp = fopen(tmp, "r");
297    if (fp == 0) return 0;
298
299    while (true) {
300        if (fgets(line, 1024, fp) == 0) {
301            break;
302        }
303
304        if (sscanf(line, "Pss: %d kB", &temp) == 1) {
305            pss += temp;
306        }
307    }
308
309    fclose(fp);
310
311    return pss;
312}
313
314static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
315{
316    return android_os_Debug_getPssPid(env, clazz, getpid());
317}
318
319static jint read_binder_stat(const char* stat)
320{
321    FILE* fp = fopen(BINDER_STATS, "r");
322    if (fp == NULL) {
323        return -1;
324    }
325
326    char line[1024];
327
328    char compare[128];
329    int len = snprintf(compare, 128, "proc %d", getpid());
330
331    // loop until we have the block that represents this process
332    do {
333        if (fgets(line, 1024, fp) == 0) {
334            return -1;
335        }
336    } while (strncmp(compare, line, len));
337
338    // now that we have this process, read until we find the stat that we are looking for
339    len = snprintf(compare, 128, "  %s: ", stat);
340
341    do {
342        if (fgets(line, 1024, fp) == 0) {
343            return -1;
344        }
345    } while (strncmp(compare, line, len));
346
347    // we have the line, now increment the line ptr to the value
348    char* ptr = line + len;
349    return atoi(ptr);
350}
351
352static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
353{
354    return read_binder_stat("bcTRANSACTION");
355}
356
357static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
358{
359    return read_binder_stat("brTRANSACTION");
360}
361
362// these are implemented in android_util_Binder.cpp
363jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
364jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
365jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
366
367
368/* pulled out of bionic */
369extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
370    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
371extern "C" void free_malloc_leak_info(uint8_t* info);
372#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
373#define BACKTRACE_SIZE          32
374
375/*
376 * This is a qsort() callback.
377 *
378 * See dumpNativeHeap() for comments about the data format and sort order.
379 */
380static int compareHeapRecords(const void* vrec1, const void* vrec2)
381{
382    const size_t* rec1 = (const size_t*) vrec1;
383    const size_t* rec2 = (const size_t*) vrec2;
384    size_t size1 = *rec1;
385    size_t size2 = *rec2;
386
387    if (size1 < size2) {
388        return 1;
389    } else if (size1 > size2) {
390        return -1;
391    }
392
393    intptr_t* bt1 = (intptr_t*)(rec1 + 2);
394    intptr_t* bt2 = (intptr_t*)(rec2 + 2);
395    for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
396        intptr_t addr1 = bt1[idx];
397        intptr_t addr2 = bt2[idx];
398        if (addr1 == addr2) {
399            if (addr1 == 0)
400                break;
401            continue;
402        }
403        if (addr1 < addr2) {
404            return -1;
405        } else if (addr1 > addr2) {
406            return 1;
407        }
408    }
409
410    return 0;
411}
412
413/*
414 * The get_malloc_leak_info() call returns an array of structs that
415 * look like this:
416 *
417 *   size_t size
418 *   size_t allocations
419 *   intptr_t backtrace[32]
420 *
421 * "size" is the size of the allocation, "backtrace" is a fixed-size
422 * array of function pointers, and "allocations" is the number of
423 * allocations with the exact same size and backtrace.
424 *
425 * The entries are sorted by descending total size (i.e. size*allocations)
426 * then allocation count.  For best results with "diff" we'd like to sort
427 * primarily by individual size then stack trace.  Since the entries are
428 * fixed-size, and we're allowed (by the current implementation) to mangle
429 * them, we can do this in place.
430 */
431static void dumpNativeHeap(FILE* fp)
432{
433    uint8_t* info = NULL;
434    size_t overallSize, infoSize, totalMemory, backtraceSize;
435
436    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
437        &backtraceSize);
438    if (info == NULL) {
439        fprintf(fp, "Native heap dump not available. To enable, run these"
440                    " commands (requires root):\n");
441        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
442        fprintf(fp, "$ adb shell stop\n");
443        fprintf(fp, "$ adb shell start\n");
444        return;
445    }
446    assert(infoSize != 0);
447    assert(overallSize % infoSize == 0);
448
449    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
450
451    size_t recordCount = overallSize / infoSize;
452    fprintf(fp, "Total memory: %zu\n", totalMemory);
453    fprintf(fp, "Allocation records: %zd\n", recordCount);
454    if (backtraceSize != BACKTRACE_SIZE) {
455        fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
456            backtraceSize, BACKTRACE_SIZE);
457    }
458    fprintf(fp, "\n");
459
460    /* re-sort the entries */
461    qsort(info, recordCount, infoSize, compareHeapRecords);
462
463    /* dump the entries to the file */
464    const uint8_t* ptr = info;
465    for (size_t idx = 0; idx < recordCount; idx++) {
466        size_t size = *(size_t*) ptr;
467        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
468        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
469
470        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
471                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
472                size & ~SIZE_FLAG_ZYGOTE_CHILD,
473                allocations);
474        for (size_t bt = 0; bt < backtraceSize; bt++) {
475            if (backtrace[bt] == 0) {
476                break;
477            } else {
478                fprintf(fp, " %08x", backtrace[bt]);
479            }
480        }
481        fprintf(fp, "\n");
482
483        ptr += infoSize;
484    }
485
486    free_malloc_leak_info(info);
487
488    fprintf(fp, "MAPS\n");
489    const char* maps = "/proc/self/maps";
490    FILE* in = fopen(maps, "r");
491    if (in == NULL) {
492        fprintf(fp, "Could not open %s\n", maps);
493        return;
494    }
495    char buf[BUFSIZ];
496    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
497        fwrite(buf, sizeof(char), n, fp);
498    }
499    fclose(in);
500
501    fprintf(fp, "END\n");
502}
503
504/*
505 * Dump the native heap, writing human-readable output to the specified
506 * file descriptor.
507 */
508static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
509    jobject fileDescriptor)
510{
511    if (fileDescriptor == NULL) {
512        jniThrowNullPointerException(env, NULL);
513        return;
514    }
515    int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
516    if (origFd < 0) {
517        jniThrowRuntimeException(env, "Invalid file descriptor");
518        return;
519    }
520
521    /* dup() the descriptor so we don't close the original with fclose() */
522    int fd = dup(origFd);
523    if (fd < 0) {
524        ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
525        jniThrowRuntimeException(env, "dup() failed");
526        return;
527    }
528
529    FILE* fp = fdopen(fd, "w");
530    if (fp == NULL) {
531        ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
532        close(fd);
533        jniThrowRuntimeException(env, "fdopen() failed");
534        return;
535    }
536
537    ALOGD("Native heap dump starting...\n");
538    dumpNativeHeap(fp);
539    ALOGD("Native heap dump complete.\n");
540
541    fclose(fp);
542}
543
544
545static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz,
546    jint pid, jstring fileName)
547{
548    if (fileName == NULL) {
549        jniThrowNullPointerException(env, NULL);
550        return;
551    }
552    const jchar* str = env->GetStringCritical(fileName, 0);
553    String8 fileName8;
554    if (str) {
555        fileName8 = String8(str, env->GetStringLength(fileName));
556        env->ReleaseStringCritical(fileName, str);
557    }
558
559    int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
560    if (fd < 0) {
561        fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
562        return;
563    }
564
565    if (lseek(fd, 0, SEEK_END) < 0) {
566        fprintf(stderr, "lseek: %s\n", strerror(errno));
567    } else {
568        dump_backtrace_to_file(pid, fd);
569    }
570
571    close(fd);
572}
573
574/*
575 * JNI registration.
576 */
577
578static JNINativeMethod gMethods[] = {
579    { "getNativeHeapSize",      "()J",
580            (void*) android_os_Debug_getNativeHeapSize },
581    { "getNativeHeapAllocatedSize", "()J",
582            (void*) android_os_Debug_getNativeHeapAllocatedSize },
583    { "getNativeHeapFreeSize",  "()J",
584            (void*) android_os_Debug_getNativeHeapFreeSize },
585    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
586            (void*) android_os_Debug_getDirtyPages },
587    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
588            (void*) android_os_Debug_getDirtyPagesPid },
589    { "getPss",                 "()J",
590            (void*) android_os_Debug_getPss },
591    { "getPss",                 "(I)J",
592            (void*) android_os_Debug_getPssPid },
593    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
594            (void*) android_os_Debug_dumpNativeHeap },
595    { "getBinderSentTransactions", "()I",
596            (void*) android_os_Debug_getBinderSentTransactions },
597    { "getBinderReceivedTransactions", "()I",
598            (void*) android_os_getBinderReceivedTransactions },
599    { "getBinderLocalObjectCount", "()I",
600            (void*)android_os_Debug_getLocalObjectCount },
601    { "getBinderProxyObjectCount", "()I",
602            (void*)android_os_Debug_getProxyObjectCount },
603    { "getBinderDeathObjectCount", "()I",
604            (void*)android_os_Debug_getDeathObjectCount },
605    { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V",
606            (void*)android_os_Debug_dumpNativeBacktraceToFile },
607};
608
609int register_android_os_Debug(JNIEnv *env)
610{
611    jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
612
613    for (int i=0; i<_NUM_CORE_HEAP; i++) {
614        stat_fields[i].pss_field =
615                env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
616        stat_fields[i].privateDirty_field =
617                env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
618        stat_fields[i].sharedDirty_field =
619                env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
620    }
621
622    otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
623
624    return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
625}
626
627}; // namespace android
628