Home | History | Annotate | Download | only in jni
      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/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>
     28 #include <errno.h>
     29 #include <assert.h>
     30 #include <ctype.h>
     31 
     32 #ifdef HAVE_MALLOC_H
     33 #include <malloc.h>
     34 #endif
     35 
     36 namespace android
     37 {
     38 
     39 enum {
     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 
     57 struct stat_fields {
     58     jfieldID pss_field;
     59     jfieldID privateDirty_field;
     60     jfieldID sharedDirty_field;
     61 };
     62 
     63 struct stat_field_names {
     64     const char* pss_name;
     65     const char* privateDirty_name;
     66     const char* sharedDirty_name;
     67 };
     68 
     69 static stat_fields stat_fields[_NUM_CORE_HEAP];
     70 
     71 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
     72     { "otherPss", "otherPrivateDirty", "otherSharedDirty" },
     73     { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" },
     74     { "nativePss", "nativePrivateDirty", "nativeSharedDirty" }
     75 };
     76 
     77 jfieldID otherStats_field;
     78 
     79 struct stats_t {
     80     int pss;
     81     int privateDirty;
     82     int sharedDirty;
     83 };
     84 
     85 #define BINDER_STATS "/proc/binder/stats"
     86 
     87 static 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 
     97 static 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 
    107 static 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 
    117 static void read_mapinfo(FILE *fp, stats_t* stats)
    118 {
    119     char line[1024];
    120     int len, nameLen;
    121     bool skip, done = false;
    122 
    123     unsigned size = 0, resident = 0, pss = 0;
    124     unsigned shared_clean = 0, shared_dirty = 0;
    125     unsigned private_clean = 0, private_dirty = 0;
    126     unsigned referenced = 0;
    127     unsigned temp;
    128 
    129     unsigned long int start;
    130     unsigned long int end = 0;
    131     unsigned long int prevEnd = 0;
    132     char* name;
    133     int name_pos;
    134 
    135     int whichHeap = HEAP_UNKNOWN;
    136     int prevHeap = HEAP_UNKNOWN;
    137 
    138     if(fgets(line, sizeof(line), fp) == 0) return;
    139 
    140     while (!done) {
    141         prevHeap = whichHeap;
    142         prevEnd = end;
    143         whichHeap = HEAP_UNKNOWN;
    144         skip = false;
    145 
    146         len = strlen(line);
    147         if (len < 1) return;
    148         line[--len] = 0;
    149 
    150         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);
    158 
    159             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             }
    185         }
    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;
    212             } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
    213                 // looks like a new mapping
    214                 // example: "10000000-10001000 ---p 10000000 00:00 0"
    215                 break;
    216             }
    217         }
    218 
    219         if (!skip) {
    220             stats[whichHeap].pss += pss;
    221             stats[whichHeap].privateDirty += private_dirty;
    222             stats[whichHeap].sharedDirty += shared_dirty;
    223         }
    224     }
    225 }
    226 
    227 static 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 
    240 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
    241         jint pid, jobject object)
    242 {
    243     stats_t stats[_NUM_HEAP];
    244     memset(&stats, 0, sizeof(stats));
    245 
    246     load_maps(pid, stats);
    247 
    248     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     }
    259 
    260     jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
    261 
    262     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);
    275 }
    276 
    277 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
    278 {
    279     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
    280 }
    281 
    282 static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid)
    283 {
    284     char line[1024];
    285     jlong pss = 0;
    286     unsigned temp;
    287 
    288     char tmp[128];
    289     FILE *fp;
    290 
    291     sprintf(tmp, "/proc/%d/smaps", pid);
    292     fp = fopen(tmp, "r");
    293     if (fp == 0) return 0;
    294 
    295     while (true) {
    296         if (fgets(line, 1024, fp) == 0) {
    297             break;
    298         }
    299 
    300         if (sscanf(line, "Pss: %d kB", &temp) == 1) {
    301             pss += temp;
    302         }
    303     }
    304 
    305     fclose(fp);
    306 
    307     return pss;
    308 }
    309 
    310 static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
    311 {
    312     return android_os_Debug_getPssPid(env, clazz, getpid());
    313 }
    314 
    315 static jint read_binder_stat(const char* stat)
    316 {
    317     FILE* fp = fopen(BINDER_STATS, "r");
    318     if (fp == NULL) {
    319         return -1;
    320     }
    321 
    322     char line[1024];
    323 
    324     char compare[128];
    325     int len = snprintf(compare, 128, "proc %d", getpid());
    326 
    327     // loop until we have the block that represents this process
    328     do {
    329         if (fgets(line, 1024, fp) == 0) {
    330             return -1;
    331         }
    332     } while (strncmp(compare, line, len));
    333 
    334     // now that we have this process, read until we find the stat that we are looking for
    335     len = snprintf(compare, 128, "  %s: ", stat);
    336 
    337     do {
    338         if (fgets(line, 1024, fp) == 0) {
    339             return -1;
    340         }
    341     } while (strncmp(compare, line, len));
    342 
    343     // we have the line, now increment the line ptr to the value
    344     char* ptr = line + len;
    345     return atoi(ptr);
    346 }
    347 
    348 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
    349 {
    350     return read_binder_stat("bcTRANSACTION");
    351 }
    352 
    353 static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
    354 {
    355     return read_binder_stat("brTRANSACTION");
    356 }
    357 
    358 // these are implemented in android_util_Binder.cpp
    359 jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
    360 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
    361 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
    362 
    363 
    364 /* pulled out of bionic */
    365 extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
    366     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
    367 extern "C" void free_malloc_leak_info(uint8_t* info);
    368 #define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
    369 #define BACKTRACE_SIZE          32
    370 
    371 /*
    372  * This is a qsort() callback.
    373  *
    374  * See dumpNativeHeap() for comments about the data format and sort order.
    375  */
    376 static int compareHeapRecords(const void* vrec1, const void* vrec2)
    377 {
    378     const size_t* rec1 = (const size_t*) vrec1;
    379     const size_t* rec2 = (const size_t*) vrec2;
    380     size_t size1 = *rec1;
    381     size_t size2 = *rec2;
    382 
    383     if (size1 < size2) {
    384         return 1;
    385     } else if (size1 > size2) {
    386         return -1;
    387     }
    388 
    389     intptr_t* bt1 = (intptr_t*)(rec1 + 2);
    390     intptr_t* bt2 = (intptr_t*)(rec2 + 2);
    391     for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
    392         intptr_t addr1 = bt1[idx];
    393         intptr_t addr2 = bt2[idx];
    394         if (addr1 == addr2) {
    395             if (addr1 == 0)
    396                 break;
    397             continue;
    398         }
    399         if (addr1 < addr2) {
    400             return -1;
    401         } else if (addr1 > addr2) {
    402             return 1;
    403         }
    404     }
    405 
    406     return 0;
    407 }
    408 
    409 /*
    410  * The get_malloc_leak_info() call returns an array of structs that
    411  * look like this:
    412  *
    413  *   size_t size
    414  *   size_t allocations
    415  *   intptr_t backtrace[32]
    416  *
    417  * "size" is the size of the allocation, "backtrace" is a fixed-size
    418  * array of function pointers, and "allocations" is the number of
    419  * allocations with the exact same size and backtrace.
    420  *
    421  * The entries are sorted by descending total size (i.e. size*allocations)
    422  * then allocation count.  For best results with "diff" we'd like to sort
    423  * primarily by individual size then stack trace.  Since the entries are
    424  * fixed-size, and we're allowed (by the current implementation) to mangle
    425  * them, we can do this in place.
    426  */
    427 static void dumpNativeHeap(FILE* fp)
    428 {
    429     uint8_t* info = NULL;
    430     size_t overallSize, infoSize, totalMemory, backtraceSize;
    431 
    432     get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
    433         &backtraceSize);
    434     if (info == NULL) {
    435         fprintf(fp, "Native heap dump not available. To enable, run these"
    436                     " commands (requires root):\n");
    437         fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
    438         fprintf(fp, "$ adb shell stop\n");
    439         fprintf(fp, "$ adb shell start\n");
    440         return;
    441     }
    442     assert(infoSize != 0);
    443     assert(overallSize % infoSize == 0);
    444 
    445     fprintf(fp, "Android Native Heap Dump v1.0\n\n");
    446 
    447     size_t recordCount = overallSize / infoSize;
    448     fprintf(fp, "Total memory: %zu\n", totalMemory);
    449     fprintf(fp, "Allocation records: %zd\n", recordCount);
    450     if (backtraceSize != BACKTRACE_SIZE) {
    451         fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
    452             backtraceSize, BACKTRACE_SIZE);
    453     }
    454     fprintf(fp, "\n");
    455 
    456     /* re-sort the entries */
    457     qsort(info, recordCount, infoSize, compareHeapRecords);
    458 
    459     /* dump the entries to the file */
    460     const uint8_t* ptr = info;
    461     for (size_t idx = 0; idx < recordCount; idx++) {
    462         size_t size = *(size_t*) ptr;
    463         size_t allocations = *(size_t*) (ptr + sizeof(size_t));
    464         intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
    465 
    466         fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
    467                 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
    468                 size & ~SIZE_FLAG_ZYGOTE_CHILD,
    469                 allocations);
    470         for (size_t bt = 0; bt < backtraceSize; bt++) {
    471             if (backtrace[bt] == 0) {
    472                 break;
    473             } else {
    474                 fprintf(fp, " %08x", backtrace[bt]);
    475             }
    476         }
    477         fprintf(fp, "\n");
    478 
    479         ptr += infoSize;
    480     }
    481 
    482     free_malloc_leak_info(info);
    483 
    484     fprintf(fp, "MAPS\n");
    485     const char* maps = "/proc/self/maps";
    486     FILE* in = fopen(maps, "r");
    487     if (in == NULL) {
    488         fprintf(fp, "Could not open %s\n", maps);
    489         return;
    490     }
    491     char buf[BUFSIZ];
    492     while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
    493         fwrite(buf, sizeof(char), n, fp);
    494     }
    495     fclose(in);
    496 
    497     fprintf(fp, "END\n");
    498 }
    499 
    500 /*
    501  * Dump the native heap, writing human-readable output to the specified
    502  * file descriptor.
    503  */
    504 static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
    505     jobject fileDescriptor)
    506 {
    507     if (fileDescriptor == NULL) {
    508         jniThrowNullPointerException(env, NULL);
    509         return;
    510     }
    511     int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    512     if (origFd < 0) {
    513         jniThrowRuntimeException(env, "Invalid file descriptor");
    514         return;
    515     }
    516 
    517     /* dup() the descriptor so we don't close the original with fclose() */
    518     int fd = dup(origFd);
    519     if (fd < 0) {
    520         LOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
    521         jniThrowRuntimeException(env, "dup() failed");
    522         return;
    523     }
    524 
    525     FILE* fp = fdopen(fd, "w");
    526     if (fp == NULL) {
    527         LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
    528         close(fd);
    529         jniThrowRuntimeException(env, "fdopen() failed");
    530         return;
    531     }
    532 
    533     LOGD("Native heap dump starting...\n");
    534     dumpNativeHeap(fp);
    535     LOGD("Native heap dump complete.\n");
    536 
    537     fclose(fp);
    538 }
    539 
    540 
    541 /*
    542  * JNI registration.
    543  */
    544 
    545 static JNINativeMethod gMethods[] = {
    546     { "getNativeHeapSize",      "()J",
    547             (void*) android_os_Debug_getNativeHeapSize },
    548     { "getNativeHeapAllocatedSize", "()J",
    549             (void*) android_os_Debug_getNativeHeapAllocatedSize },
    550     { "getNativeHeapFreeSize",  "()J",
    551             (void*) android_os_Debug_getNativeHeapFreeSize },
    552     { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
    553             (void*) android_os_Debug_getDirtyPages },
    554     { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
    555             (void*) android_os_Debug_getDirtyPagesPid },
    556     { "getPss",                 "()J",
    557             (void*) android_os_Debug_getPss },
    558     { "getPss",                 "(I)J",
    559             (void*) android_os_Debug_getPssPid },
    560     { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
    561             (void*) android_os_Debug_dumpNativeHeap },
    562     { "getBinderSentTransactions", "()I",
    563             (void*) android_os_Debug_getBinderSentTransactions },
    564     { "getBinderReceivedTransactions", "()I",
    565             (void*) android_os_getBinderReceivedTransactions },
    566     { "getBinderLocalObjectCount", "()I",
    567             (void*)android_os_Debug_getLocalObjectCount },
    568     { "getBinderProxyObjectCount", "()I",
    569             (void*)android_os_Debug_getProxyObjectCount },
    570     { "getBinderDeathObjectCount", "()I",
    571             (void*)android_os_Debug_getDeathObjectCount },
    572 };
    573 
    574 int register_android_os_Debug(JNIEnv *env)
    575 {
    576     jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
    577 
    578     for (int i=0; i<_NUM_CORE_HEAP; i++) {
    579         stat_fields[i].pss_field =
    580                 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
    581         stat_fields[i].privateDirty_field =
    582                 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
    583         stat_fields[i].sharedDirty_field =
    584                 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
    585     }
    586 
    587     otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
    588 
    589     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
    590 }
    591 
    592 }; // namespace android
    593