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