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