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