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