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