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/String8.h>
     21 #include "utils/misc.h"
     22 #include "cutils/debugger.h"
     23 #include <memtrack/memtrack.h>
     24 
     25 #include <cutils/log.h>
     26 #include <fcntl.h>
     27 #include <inttypes.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <unistd.h>
     32 #include <time.h>
     33 #include <sys/time.h>
     34 #include <errno.h>
     35 #include <assert.h>
     36 #include <ctype.h>
     37 
     38 #ifdef HAVE_MALLOC_H
     39 #include <malloc.h>
     40 #endif
     41 
     42 namespace android
     43 {
     44 
     45 enum {
     46     HEAP_UNKNOWN,
     47     HEAP_DALVIK,
     48     HEAP_NATIVE,
     49     HEAP_DALVIK_OTHER,
     50     HEAP_STACK,
     51     HEAP_CURSOR,
     52     HEAP_ASHMEM,
     53     HEAP_UNKNOWN_DEV,
     54     HEAP_SO,
     55     HEAP_JAR,
     56     HEAP_APK,
     57     HEAP_TTF,
     58     HEAP_DEX,
     59     HEAP_OAT,
     60     HEAP_ART,
     61     HEAP_UNKNOWN_MAP,
     62     HEAP_GRAPHICS,
     63     HEAP_GL,
     64     HEAP_OTHER_MEMTRACK,
     65 
     66     HEAP_DALVIK_NORMAL,
     67     HEAP_DALVIK_LARGE,
     68     HEAP_DALVIK_LINEARALLOC,
     69     HEAP_DALVIK_ACCOUNTING,
     70     HEAP_DALVIK_CODE_CACHE,
     71 
     72     _NUM_HEAP,
     73     _NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1,
     74     _NUM_CORE_HEAP = HEAP_NATIVE+1
     75 };
     76 
     77 struct stat_fields {
     78     jfieldID pss_field;
     79     jfieldID pssSwappable_field;
     80     jfieldID privateDirty_field;
     81     jfieldID sharedDirty_field;
     82     jfieldID privateClean_field;
     83     jfieldID sharedClean_field;
     84     jfieldID swappedOut_field;
     85 };
     86 
     87 struct stat_field_names {
     88     const char* pss_name;
     89     const char* pssSwappable_name;
     90     const char* privateDirty_name;
     91     const char* sharedDirty_name;
     92     const char* privateClean_name;
     93     const char* sharedClean_name;
     94     const char* swappedOut_name;
     95 };
     96 
     97 static stat_fields stat_fields[_NUM_CORE_HEAP];
     98 
     99 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
    100     { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty",
    101         "otherPrivateClean", "otherSharedClean", "otherSwappedOut" },
    102     { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty",
    103         "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut" },
    104     { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty",
    105         "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut" }
    106 };
    107 
    108 jfieldID otherStats_field;
    109 
    110 static bool memtrackLoaded;
    111 
    112 struct stats_t {
    113     int pss;
    114     int swappablePss;
    115     int privateDirty;
    116     int sharedDirty;
    117     int privateClean;
    118     int sharedClean;
    119     int swappedOut;
    120 };
    121 
    122 #define BINDER_STATS "/proc/binder/stats"
    123 
    124 static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
    125 {
    126 #ifdef HAVE_MALLOC_H
    127     struct mallinfo info = mallinfo();
    128     return (jlong) info.usmblks;
    129 #else
    130     return -1;
    131 #endif
    132 }
    133 
    134 static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
    135 {
    136 #ifdef HAVE_MALLOC_H
    137     struct mallinfo info = mallinfo();
    138     return (jlong) info.uordblks;
    139 #else
    140     return -1;
    141 #endif
    142 }
    143 
    144 static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
    145 {
    146 #ifdef HAVE_MALLOC_H
    147     struct mallinfo info = mallinfo();
    148     return (jlong) info.fordblks;
    149 #else
    150     return -1;
    151 #endif
    152 }
    153 
    154 // Container used to retrieve graphics memory pss
    155 struct graphics_memory_pss
    156 {
    157     int graphics;
    158     int gl;
    159     int other;
    160 };
    161 
    162 /*
    163  * Uses libmemtrack to retrieve graphics memory that the process is using.
    164  * Any graphics memory reported in /proc/pid/smaps is not included here.
    165  */
    166 static int read_memtrack_memory(struct memtrack_proc* p, int pid,
    167         struct graphics_memory_pss* graphics_mem)
    168 {
    169     int err = memtrack_proc_get(p, pid);
    170     if (err != 0) {
    171         ALOGW("failed to get memory consumption info: %d", err);
    172         return err;
    173     }
    174 
    175     ssize_t pss = memtrack_proc_graphics_pss(p);
    176     if (pss < 0) {
    177         ALOGW("failed to get graphics pss: %zd", pss);
    178         return pss;
    179     }
    180     graphics_mem->graphics = pss / 1024;
    181 
    182     pss = memtrack_proc_gl_pss(p);
    183     if (pss < 0) {
    184         ALOGW("failed to get gl pss: %zd", pss);
    185         return pss;
    186     }
    187     graphics_mem->gl = pss / 1024;
    188 
    189     pss = memtrack_proc_other_pss(p);
    190     if (pss < 0) {
    191         ALOGW("failed to get other pss: %zd", pss);
    192         return pss;
    193     }
    194     graphics_mem->other = pss / 1024;
    195 
    196     return 0;
    197 }
    198 
    199 /*
    200  * Retrieves the graphics memory that is unaccounted for in /proc/pid/smaps.
    201  */
    202 static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_mem)
    203 {
    204     if (!memtrackLoaded) {
    205         return -1;
    206     }
    207 
    208     struct memtrack_proc* p = memtrack_proc_new();
    209     if (p == NULL) {
    210         ALOGW("failed to create memtrack_proc");
    211         return -1;
    212     }
    213 
    214     int err = read_memtrack_memory(p, pid, graphics_mem);
    215     memtrack_proc_destroy(p);
    216     return err;
    217 }
    218 
    219 static void read_mapinfo(FILE *fp, stats_t* stats)
    220 {
    221     char line[1024];
    222     int len, nameLen;
    223     bool skip, done = false;
    224 
    225     unsigned size = 0, resident = 0, pss = 0, swappable_pss = 0;
    226     float sharing_proportion = 0.0;
    227     unsigned shared_clean = 0, shared_dirty = 0;
    228     unsigned private_clean = 0, private_dirty = 0;
    229     unsigned swapped_out = 0;
    230     bool is_swappable = false;
    231     unsigned referenced = 0;
    232     unsigned temp;
    233 
    234     uint64_t start;
    235     uint64_t end = 0;
    236     uint64_t prevEnd = 0;
    237     char* name;
    238     int name_pos;
    239 
    240     int whichHeap = HEAP_UNKNOWN;
    241     int subHeap = HEAP_UNKNOWN;
    242     int prevHeap = HEAP_UNKNOWN;
    243 
    244     if(fgets(line, sizeof(line), fp) == 0) return;
    245 
    246     while (!done) {
    247         prevHeap = whichHeap;
    248         prevEnd = end;
    249         whichHeap = HEAP_UNKNOWN;
    250         subHeap = HEAP_UNKNOWN;
    251         skip = false;
    252         is_swappable = false;
    253 
    254         len = strlen(line);
    255         if (len < 1) return;
    256         line[--len] = 0;
    257 
    258         if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
    259             skip = true;
    260         } else {
    261             while (isspace(line[name_pos])) {
    262                 name_pos += 1;
    263             }
    264             name = line + name_pos;
    265             nameLen = strlen(name);
    266 
    267             if ((strstr(name, "[heap]") == name)) {
    268                 whichHeap = HEAP_NATIVE;
    269             } else if (strncmp(name, "/dev/ashmem", 11) == 0) {
    270                 if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) {
    271                     whichHeap = HEAP_DALVIK_OTHER;
    272                     if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) {
    273                         subHeap = HEAP_DALVIK_LINEARALLOC;
    274                     } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) ||
    275                                (strstr(name, "/dev/ashmem/dalvik-main space") == name) ||
    276                                (strstr(name, "/dev/ashmem/dalvik-non moving space") == name)) {
    277                         // This is the regular Dalvik heap.
    278                         whichHeap = HEAP_DALVIK;
    279                         subHeap = HEAP_DALVIK_NORMAL;
    280                     } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name) {
    281                         whichHeap = HEAP_DALVIK;
    282                         subHeap = HEAP_DALVIK_LARGE;
    283                     } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name) {
    284                         subHeap = HEAP_DALVIK_CODE_CACHE;
    285                     } else {
    286                         subHeap = HEAP_DALVIK_ACCOUNTING;  // Default to accounting.
    287                     }
    288                 } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
    289                     whichHeap = HEAP_CURSOR;
    290                 } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) {
    291                     whichHeap = HEAP_NATIVE;
    292                 } else {
    293                     whichHeap = HEAP_ASHMEM;
    294                 }
    295             } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) {
    296                 whichHeap = HEAP_NATIVE;
    297             } else if (strncmp(name, "[stack", 6) == 0) {
    298                 whichHeap = HEAP_STACK;
    299             } else if (strncmp(name, "/dev/", 5) == 0) {
    300                 whichHeap = HEAP_UNKNOWN_DEV;
    301             } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
    302                 whichHeap = HEAP_SO;
    303                 is_swappable = true;
    304             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
    305                 whichHeap = HEAP_JAR;
    306                 is_swappable = true;
    307             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
    308                 whichHeap = HEAP_APK;
    309                 is_swappable = true;
    310             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
    311                 whichHeap = HEAP_TTF;
    312                 is_swappable = true;
    313             } else if ((nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) ||
    314                        (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
    315                 whichHeap = HEAP_DEX;
    316                 is_swappable = true;
    317             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
    318                 whichHeap = HEAP_OAT;
    319                 is_swappable = true;
    320             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
    321                 whichHeap = HEAP_ART;
    322                 is_swappable = true;
    323             } else if (strncmp(name, "[anon:", 6) == 0) {
    324                 whichHeap = HEAP_UNKNOWN;
    325             } else if (nameLen > 0) {
    326                 whichHeap = HEAP_UNKNOWN_MAP;
    327             } else if (start == prevEnd && prevHeap == HEAP_SO) {
    328                 // bss section of a shared library.
    329                 whichHeap = HEAP_SO;
    330             }
    331         }
    332 
    333         //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
    334         //    isSqliteHeap, line);
    335 
    336         shared_clean = 0;
    337         shared_dirty = 0;
    338         private_clean = 0;
    339         private_dirty = 0;
    340         swapped_out = 0;
    341 
    342         while (true) {
    343             if (fgets(line, 1024, fp) == 0) {
    344                 done = true;
    345                 break;
    346             }
    347 
    348             if (line[0] == 'S' && sscanf(line, "Size: %d kB", &temp) == 1) {
    349                 size = temp;
    350             } else if (line[0] == 'R' && sscanf(line, "Rss: %d kB", &temp) == 1) {
    351                 resident = temp;
    352             } else if (line[0] == 'P' && sscanf(line, "Pss: %d kB", &temp) == 1) {
    353                 pss = temp;
    354             } else if (line[0] == 'S' && sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
    355                 shared_clean = temp;
    356             } else if (line[0] == 'S' && sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
    357                 shared_dirty = temp;
    358             } else if (line[0] == 'P' && sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
    359                 private_clean = temp;
    360             } else if (line[0] == 'P' && sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
    361                 private_dirty = temp;
    362             } else if (line[0] == 'R' && sscanf(line, "Referenced: %d kB", &temp) == 1) {
    363                 referenced = temp;
    364             } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
    365                 swapped_out = temp;
    366             } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
    367                 // looks like a new mapping
    368                 // example: "10000000-10001000 ---p 10000000 00:00 0"
    369                 break;
    370             }
    371         }
    372 
    373         if (!skip) {
    374             if (is_swappable && (pss > 0)) {
    375                 sharing_proportion = 0.0;
    376                 if ((shared_clean > 0) || (shared_dirty > 0)) {
    377                     sharing_proportion = (pss - private_clean
    378                             - private_dirty)/(shared_clean+shared_dirty);
    379                 }
    380                 swappable_pss = (sharing_proportion*shared_clean) + private_clean;
    381             } else
    382                 swappable_pss = 0;
    383 
    384             stats[whichHeap].pss += pss;
    385             stats[whichHeap].swappablePss += swappable_pss;
    386             stats[whichHeap].privateDirty += private_dirty;
    387             stats[whichHeap].sharedDirty += shared_dirty;
    388             stats[whichHeap].privateClean += private_clean;
    389             stats[whichHeap].sharedClean += shared_clean;
    390             stats[whichHeap].swappedOut += swapped_out;
    391             if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
    392                 stats[subHeap].pss += pss;
    393                 stats[subHeap].swappablePss += swappable_pss;
    394                 stats[subHeap].privateDirty += private_dirty;
    395                 stats[subHeap].sharedDirty += shared_dirty;
    396                 stats[subHeap].privateClean += private_clean;
    397                 stats[subHeap].sharedClean += shared_clean;
    398                 stats[subHeap].swappedOut += swapped_out;
    399             }
    400         }
    401     }
    402 }
    403 
    404 static void load_maps(int pid, stats_t* stats)
    405 {
    406     char tmp[128];
    407     FILE *fp;
    408 
    409     sprintf(tmp, "/proc/%d/smaps", pid);
    410     fp = fopen(tmp, "r");
    411     if (fp == 0) return;
    412 
    413     read_mapinfo(fp, stats);
    414     fclose(fp);
    415 }
    416 
    417 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
    418         jint pid, jobject object)
    419 {
    420     stats_t stats[_NUM_HEAP];
    421     memset(&stats, 0, sizeof(stats));
    422 
    423     load_maps(pid, stats);
    424 
    425     struct graphics_memory_pss graphics_mem;
    426     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
    427         stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
    428         stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
    429         stats[HEAP_GL].pss = graphics_mem.gl;
    430         stats[HEAP_GL].privateDirty = graphics_mem.gl;
    431         stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
    432         stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
    433     }
    434 
    435     for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
    436         stats[HEAP_UNKNOWN].pss += stats[i].pss;
    437         stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
    438         stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
    439         stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
    440         stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
    441         stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
    442         stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
    443     }
    444 
    445     for (int i=0; i<_NUM_CORE_HEAP; i++) {
    446         env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
    447         env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
    448         env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
    449         env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
    450         env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
    451         env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
    452         env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
    453     }
    454 
    455 
    456     jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
    457 
    458     jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
    459     if (otherArray == NULL) {
    460         return;
    461     }
    462 
    463     int j=0;
    464     for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
    465         otherArray[j++] = stats[i].pss;
    466         otherArray[j++] = stats[i].swappablePss;
    467         otherArray[j++] = stats[i].privateDirty;
    468         otherArray[j++] = stats[i].sharedDirty;
    469         otherArray[j++] = stats[i].privateClean;
    470         otherArray[j++] = stats[i].sharedClean;
    471         otherArray[j++] = stats[i].swappedOut;
    472     }
    473 
    474     env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
    475 }
    476 
    477 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
    478 {
    479     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
    480 }
    481 
    482 static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss)
    483 {
    484     char line[1024];
    485     jlong pss = 0;
    486     jlong uss = 0;
    487     unsigned temp;
    488 
    489     char tmp[128];
    490     FILE *fp;
    491 
    492     struct graphics_memory_pss graphics_mem;
    493     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
    494         pss = uss = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
    495     }
    496 
    497     sprintf(tmp, "/proc/%d/smaps", pid);
    498     fp = fopen(tmp, "r");
    499 
    500     if (fp != 0) {
    501         while (true) {
    502             if (fgets(line, 1024, fp) == NULL) {
    503                 break;
    504             }
    505 
    506             if (line[0] == 'P') {
    507                 if (strncmp(line, "Pss:", 4) == 0) {
    508                     char* c = line + 4;
    509                     while (*c != 0 && (*c < '0' || *c > '9')) {
    510                         c++;
    511                     }
    512                     pss += atoi(c);
    513                 } else if (strncmp(line, "Private_Clean:", 14)
    514                         || strncmp(line, "Private_Dirty:", 14)) {
    515                     char* c = line + 14;
    516                     while (*c != 0 && (*c < '0' || *c > '9')) {
    517                         c++;
    518                     }
    519                     uss += atoi(c);
    520                 }
    521             }
    522         }
    523 
    524         fclose(fp);
    525     }
    526 
    527     if (outUss != NULL) {
    528         if (env->GetArrayLength(outUss) >= 1) {
    529             jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
    530             if (outUssArray != NULL) {
    531                 outUssArray[0] = uss;
    532             }
    533             env->ReleaseLongArrayElements(outUss, outUssArray, 0);
    534         }
    535     }
    536 
    537     return pss;
    538 }
    539 
    540 static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
    541 {
    542     return android_os_Debug_getPssPid(env, clazz, getpid(), NULL);
    543 }
    544 
    545 enum {
    546     MEMINFO_TOTAL,
    547     MEMINFO_FREE,
    548     MEMINFO_BUFFERS,
    549     MEMINFO_CACHED,
    550     MEMINFO_SHMEM,
    551     MEMINFO_SLAB,
    552     MEMINFO_SWAP_TOTAL,
    553     MEMINFO_SWAP_FREE,
    554     MEMINFO_ZRAM_TOTAL,
    555     MEMINFO_COUNT
    556 };
    557 
    558 static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
    559 {
    560     char buffer[1024];
    561     int numFound = 0;
    562 
    563     if (out == NULL) {
    564         jniThrowNullPointerException(env, "out == null");
    565         return;
    566     }
    567 
    568     int fd = open("/proc/meminfo", O_RDONLY);
    569 
    570     if (fd < 0) {
    571         ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
    572         return;
    573     }
    574 
    575     int len = read(fd, buffer, sizeof(buffer)-1);
    576     close(fd);
    577 
    578     if (len < 0) {
    579         ALOGW("Empty /proc/meminfo");
    580         return;
    581     }
    582     buffer[len] = 0;
    583 
    584     static const char* const tags[] = {
    585             "MemTotal:",
    586             "MemFree:",
    587             "Buffers:",
    588             "Cached:",
    589             "Shmem:",
    590             "Slab:",
    591             "SwapTotal:",
    592             "SwapFree:",
    593             NULL
    594     };
    595     static const int tagsLen[] = {
    596             9,
    597             8,
    598             8,
    599             7,
    600             6,
    601             5,
    602             10,
    603             9,
    604             0
    605     };
    606     long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    607 
    608     char* p = buffer;
    609     while (*p && numFound < 8) {
    610         int i = 0;
    611         while (tags[i]) {
    612             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
    613                 p += tagsLen[i];
    614                 while (*p == ' ') p++;
    615                 char* num = p;
    616                 while (*p >= '0' && *p <= '9') p++;
    617                 if (*p != 0) {
    618                     *p = 0;
    619                     p++;
    620                 }
    621                 mem[i] = atoll(num);
    622                 numFound++;
    623                 break;
    624             }
    625             i++;
    626         }
    627         while (*p && *p != '\n') {
    628             p++;
    629         }
    630         if (*p) p++;
    631     }
    632 
    633     fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
    634     if (fd >= 0) {
    635         len = read(fd, buffer, sizeof(buffer)-1);
    636         close(fd);
    637         if (len > 0) {
    638             buffer[len] = 0;
    639             mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
    640         }
    641     }
    642 
    643     int maxNum = env->GetArrayLength(out);
    644     if (maxNum > MEMINFO_COUNT) {
    645         maxNum = MEMINFO_COUNT;
    646     }
    647     jlong* outArray = env->GetLongArrayElements(out, 0);
    648     if (outArray != NULL) {
    649         for (int i=0; i<maxNum; i++) {
    650             outArray[i] = mem[i];
    651         }
    652     }
    653     env->ReleaseLongArrayElements(out, outArray, 0);
    654 }
    655 
    656 static jint read_binder_stat(const char* stat)
    657 {
    658     FILE* fp = fopen(BINDER_STATS, "r");
    659     if (fp == NULL) {
    660         return -1;
    661     }
    662 
    663     char line[1024];
    664 
    665     char compare[128];
    666     int len = snprintf(compare, 128, "proc %d", getpid());
    667 
    668     // loop until we have the block that represents this process
    669     do {
    670         if (fgets(line, 1024, fp) == 0) {
    671             fclose(fp);
    672             return -1;
    673         }
    674     } while (strncmp(compare, line, len));
    675 
    676     // now that we have this process, read until we find the stat that we are looking for
    677     len = snprintf(compare, 128, "  %s: ", stat);
    678 
    679     do {
    680         if (fgets(line, 1024, fp) == 0) {
    681             fclose(fp);
    682             return -1;
    683         }
    684     } while (strncmp(compare, line, len));
    685 
    686     // we have the line, now increment the line ptr to the value
    687     char* ptr = line + len;
    688     jint result = atoi(ptr);
    689     fclose(fp);
    690     return result;
    691 }
    692 
    693 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
    694 {
    695     return read_binder_stat("bcTRANSACTION");
    696 }
    697 
    698 static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
    699 {
    700     return read_binder_stat("brTRANSACTION");
    701 }
    702 
    703 // these are implemented in android_util_Binder.cpp
    704 jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
    705 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
    706 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
    707 
    708 
    709 /* pulled out of bionic */
    710 extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
    711     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
    712 extern "C" void free_malloc_leak_info(uint8_t* info);
    713 #define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
    714 #define BACKTRACE_SIZE          32
    715 
    716 /*
    717  * This is a qsort() callback.
    718  *
    719  * See dumpNativeHeap() for comments about the data format and sort order.
    720  */
    721 static int compareHeapRecords(const void* vrec1, const void* vrec2)
    722 {
    723     const size_t* rec1 = (const size_t*) vrec1;
    724     const size_t* rec2 = (const size_t*) vrec2;
    725     size_t size1 = *rec1;
    726     size_t size2 = *rec2;
    727 
    728     if (size1 < size2) {
    729         return 1;
    730     } else if (size1 > size2) {
    731         return -1;
    732     }
    733 
    734     intptr_t* bt1 = (intptr_t*)(rec1 + 2);
    735     intptr_t* bt2 = (intptr_t*)(rec2 + 2);
    736     for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
    737         intptr_t addr1 = bt1[idx];
    738         intptr_t addr2 = bt2[idx];
    739         if (addr1 == addr2) {
    740             if (addr1 == 0)
    741                 break;
    742             continue;
    743         }
    744         if (addr1 < addr2) {
    745             return -1;
    746         } else if (addr1 > addr2) {
    747             return 1;
    748         }
    749     }
    750 
    751     return 0;
    752 }
    753 
    754 /*
    755  * The get_malloc_leak_info() call returns an array of structs that
    756  * look like this:
    757  *
    758  *   size_t size
    759  *   size_t allocations
    760  *   intptr_t backtrace[32]
    761  *
    762  * "size" is the size of the allocation, "backtrace" is a fixed-size
    763  * array of function pointers, and "allocations" is the number of
    764  * allocations with the exact same size and backtrace.
    765  *
    766  * The entries are sorted by descending total size (i.e. size*allocations)
    767  * then allocation count.  For best results with "diff" we'd like to sort
    768  * primarily by individual size then stack trace.  Since the entries are
    769  * fixed-size, and we're allowed (by the current implementation) to mangle
    770  * them, we can do this in place.
    771  */
    772 static void dumpNativeHeap(FILE* fp)
    773 {
    774     uint8_t* info = NULL;
    775     size_t overallSize, infoSize, totalMemory, backtraceSize;
    776 
    777     get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
    778         &backtraceSize);
    779     if (info == NULL) {
    780         fprintf(fp, "Native heap dump not available. To enable, run these"
    781                     " commands (requires root):\n");
    782         fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
    783         fprintf(fp, "$ adb shell stop\n");
    784         fprintf(fp, "$ adb shell start\n");
    785         return;
    786     }
    787     assert(infoSize != 0);
    788     assert(overallSize % infoSize == 0);
    789 
    790     fprintf(fp, "Android Native Heap Dump v1.0\n\n");
    791 
    792     size_t recordCount = overallSize / infoSize;
    793     fprintf(fp, "Total memory: %zu\n", totalMemory);
    794     fprintf(fp, "Allocation records: %zd\n", recordCount);
    795     if (backtraceSize != BACKTRACE_SIZE) {
    796         fprintf(fp, "WARNING: mismatched backtrace sizes (%zu vs. %d)\n",
    797             backtraceSize, BACKTRACE_SIZE);
    798     }
    799     fprintf(fp, "\n");
    800 
    801     /* re-sort the entries */
    802     qsort(info, recordCount, infoSize, compareHeapRecords);
    803 
    804     /* dump the entries to the file */
    805     const uint8_t* ptr = info;
    806     for (size_t idx = 0; idx < recordCount; idx++) {
    807         size_t size = *(size_t*) ptr;
    808         size_t allocations = *(size_t*) (ptr + sizeof(size_t));
    809         intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
    810 
    811         fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
    812                 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
    813                 size & ~SIZE_FLAG_ZYGOTE_CHILD,
    814                 allocations);
    815         for (size_t bt = 0; bt < backtraceSize; bt++) {
    816             if (backtrace[bt] == 0) {
    817                 break;
    818             } else {
    819 #ifdef __LP64__
    820                 fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
    821 #else
    822                 fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
    823 #endif
    824             }
    825         }
    826         fprintf(fp, "\n");
    827 
    828         ptr += infoSize;
    829     }
    830 
    831     free_malloc_leak_info(info);
    832 
    833     fprintf(fp, "MAPS\n");
    834     const char* maps = "/proc/self/maps";
    835     FILE* in = fopen(maps, "r");
    836     if (in == NULL) {
    837         fprintf(fp, "Could not open %s\n", maps);
    838         return;
    839     }
    840     char buf[BUFSIZ];
    841     while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
    842         fwrite(buf, sizeof(char), n, fp);
    843     }
    844     fclose(in);
    845 
    846     fprintf(fp, "END\n");
    847 }
    848 
    849 /*
    850  * Dump the native heap, writing human-readable output to the specified
    851  * file descriptor.
    852  */
    853 static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
    854     jobject fileDescriptor)
    855 {
    856     if (fileDescriptor == NULL) {
    857         jniThrowNullPointerException(env, "fd == null");
    858         return;
    859     }
    860     int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    861     if (origFd < 0) {
    862         jniThrowRuntimeException(env, "Invalid file descriptor");
    863         return;
    864     }
    865 
    866     /* dup() the descriptor so we don't close the original with fclose() */
    867     int fd = dup(origFd);
    868     if (fd < 0) {
    869         ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
    870         jniThrowRuntimeException(env, "dup() failed");
    871         return;
    872     }
    873 
    874     FILE* fp = fdopen(fd, "w");
    875     if (fp == NULL) {
    876         ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
    877         close(fd);
    878         jniThrowRuntimeException(env, "fdopen() failed");
    879         return;
    880     }
    881 
    882     ALOGD("Native heap dump starting...\n");
    883     dumpNativeHeap(fp);
    884     ALOGD("Native heap dump complete.\n");
    885 
    886     fclose(fp);
    887 }
    888 
    889 
    890 static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz,
    891     jint pid, jstring fileName)
    892 {
    893     if (fileName == NULL) {
    894         jniThrowNullPointerException(env, "file == null");
    895         return;
    896     }
    897     const jchar* str = env->GetStringCritical(fileName, 0);
    898     String8 fileName8;
    899     if (str) {
    900         fileName8 = String8(str, env->GetStringLength(fileName));
    901         env->ReleaseStringCritical(fileName, str);
    902     }
    903 
    904     int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
    905     if (fd < 0) {
    906         fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
    907         return;
    908     }
    909 
    910     if (lseek(fd, 0, SEEK_END) < 0) {
    911         fprintf(stderr, "lseek: %s\n", strerror(errno));
    912     } else {
    913         dump_backtrace_to_file(pid, fd);
    914     }
    915 
    916     close(fd);
    917 }
    918 
    919 /*
    920  * JNI registration.
    921  */
    922 
    923 static JNINativeMethod gMethods[] = {
    924     { "getNativeHeapSize",      "()J",
    925             (void*) android_os_Debug_getNativeHeapSize },
    926     { "getNativeHeapAllocatedSize", "()J",
    927             (void*) android_os_Debug_getNativeHeapAllocatedSize },
    928     { "getNativeHeapFreeSize",  "()J",
    929             (void*) android_os_Debug_getNativeHeapFreeSize },
    930     { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
    931             (void*) android_os_Debug_getDirtyPages },
    932     { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
    933             (void*) android_os_Debug_getDirtyPagesPid },
    934     { "getPss",                 "()J",
    935             (void*) android_os_Debug_getPss },
    936     { "getPss",                 "(I[J)J",
    937             (void*) android_os_Debug_getPssPid },
    938     { "getMemInfo",             "([J)V",
    939             (void*) android_os_Debug_getMemInfo },
    940     { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
    941             (void*) android_os_Debug_dumpNativeHeap },
    942     { "getBinderSentTransactions", "()I",
    943             (void*) android_os_Debug_getBinderSentTransactions },
    944     { "getBinderReceivedTransactions", "()I",
    945             (void*) android_os_getBinderReceivedTransactions },
    946     { "getBinderLocalObjectCount", "()I",
    947             (void*)android_os_Debug_getLocalObjectCount },
    948     { "getBinderProxyObjectCount", "()I",
    949             (void*)android_os_Debug_getProxyObjectCount },
    950     { "getBinderDeathObjectCount", "()I",
    951             (void*)android_os_Debug_getDeathObjectCount },
    952     { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V",
    953             (void*)android_os_Debug_dumpNativeBacktraceToFile },
    954 };
    955 
    956 int register_android_os_Debug(JNIEnv *env)
    957 {
    958     int err = memtrack_init();
    959     if (err != 0) {
    960         memtrackLoaded = false;
    961         ALOGE("failed to load memtrack module: %d", err);
    962     } else {
    963         memtrackLoaded = true;
    964     }
    965 
    966     jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
    967 
    968     // Sanity check the number of other statistics expected in Java matches here.
    969     jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
    970     jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
    971     jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
    972     jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
    973     int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
    974     if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
    975         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    976                              "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
    977                              numOtherStats+numDvkStats, expectedNumOtherStats);
    978         return JNI_ERR;
    979     }
    980 
    981     otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
    982 
    983     for (int i=0; i<_NUM_CORE_HEAP; i++) {
    984         stat_fields[i].pss_field =
    985                 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
    986         stat_fields[i].pssSwappable_field =
    987                 env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
    988         stat_fields[i].privateDirty_field =
    989                 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
    990         stat_fields[i].sharedDirty_field =
    991                 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
    992         stat_fields[i].privateClean_field =
    993                 env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
    994         stat_fields[i].sharedClean_field =
    995                 env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
    996         stat_fields[i].swappedOut_field =
    997                 env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
    998     }
    999 
   1000     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
   1001 }
   1002 
   1003 }; // namespace android
   1004