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 #include "JNIHelp.h"
     18 #include "jni.h"
     19 #include "utils/misc.h"
     20 
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <unistd.h>
     25 #include <time.h>
     26 #include <sys/time.h>
     27 
     28 #ifdef HAVE_MALLOC_H
     29 #include <malloc.h>
     30 #endif
     31 
     32 namespace android
     33 {
     34 
     35 static jfieldID dalvikPss_field;
     36 static jfieldID dalvikPrivateDirty_field;
     37 static jfieldID dalvikSharedDirty_field;
     38 static jfieldID nativePss_field;
     39 static jfieldID nativePrivateDirty_field;
     40 static jfieldID nativeSharedDirty_field;
     41 static jfieldID otherPss_field;
     42 static jfieldID otherPrivateDirty_field;
     43 static jfieldID otherSharedDirty_field;
     44 
     45 struct stats_t {
     46     int dalvikPss;
     47     int dalvikPrivateDirty;
     48     int dalvikSharedDirty;
     49 
     50     int nativePss;
     51     int nativePrivateDirty;
     52     int nativeSharedDirty;
     53 
     54     int otherPss;
     55     int otherPrivateDirty;
     56     int otherSharedDirty;
     57 };
     58 
     59 #define BINDER_STATS "/proc/binder/stats"
     60 
     61 static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
     62 {
     63 #ifdef HAVE_MALLOC_H
     64     struct mallinfo info = mallinfo();
     65     return (jlong) info.usmblks;
     66 #else
     67     return -1;
     68 #endif
     69 }
     70 
     71 static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
     72 {
     73 #ifdef HAVE_MALLOC_H
     74     struct mallinfo info = mallinfo();
     75     return (jlong) info.uordblks;
     76 #else
     77     return -1;
     78 #endif
     79 }
     80 
     81 static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
     82 {
     83 #ifdef HAVE_MALLOC_H
     84     struct mallinfo info = mallinfo();
     85     return (jlong) info.fordblks;
     86 #else
     87     return -1;
     88 #endif
     89 }
     90 
     91 static void read_mapinfo(FILE *fp, stats_t* stats)
     92 {
     93     char line[1024];
     94     int len;
     95     bool skip, done = false;
     96 
     97     unsigned start = 0, size = 0, resident = 0, pss = 0;
     98     unsigned shared_clean = 0, shared_dirty = 0;
     99     unsigned private_clean = 0, private_dirty = 0;
    100     unsigned referenced = 0;
    101     unsigned temp;
    102 
    103     int isNativeHeap;
    104     int isDalvikHeap;
    105     int isSqliteHeap;
    106 
    107     if(fgets(line, 1024, fp) == 0) return;
    108 
    109     while (!done) {
    110         isNativeHeap = 0;
    111         isDalvikHeap = 0;
    112         isSqliteHeap = 0;
    113         skip = false;
    114 
    115         len = strlen(line);
    116         if (len < 1) return;
    117         line[--len] = 0;
    118 
    119         /* ignore guard pages */
    120         if (len > 18 && line[17] == '-') skip = true;
    121 
    122         start = strtoul(line, 0, 16);
    123 
    124         if (strstr(line, "[heap]")) {
    125             isNativeHeap = 1;
    126         } else if (strstr(line, "/dalvik-LinearAlloc")) {
    127             isDalvikHeap = 1;
    128         } else if (strstr(line, "/mspace/dalvik-heap")) {
    129             isDalvikHeap = 1;
    130         } else if (strstr(line, "/dalvik-heap-bitmap/")) {
    131             isDalvikHeap = 1;
    132         } else if (strstr(line, "/data/dalvik-cache/")) {
    133             isDalvikHeap = 1;
    134         } else if (strstr(line, "/tmp/sqlite-heap")) {
    135             isSqliteHeap = 1;
    136         }
    137 
    138         //LOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
    139         //    isSqliteHeap, line);
    140 
    141         while (true) {
    142             if (fgets(line, 1024, fp) == 0) {
    143                 done = true;
    144                 break;
    145             }
    146 
    147             if (sscanf(line, "Size: %d kB", &temp) == 1) {
    148                 size = temp;
    149             } else if (sscanf(line, "Rss: %d kB", &temp) == 1) {
    150                 resident = temp;
    151             } else if (sscanf(line, "Pss: %d kB", &temp) == 1) {
    152                 pss = temp;
    153             } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
    154                 shared_clean = temp;
    155             } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
    156                 shared_dirty = temp;
    157             } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
    158                 private_clean = temp;
    159             } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
    160                 private_dirty = temp;
    161             } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) {
    162                 referenced = temp;
    163             } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
    164                 // looks like a new mapping
    165                 // example: "10000000-10001000 ---p 10000000 00:00 0"
    166                 break;
    167             }
    168         }
    169 
    170         if (!skip) {
    171             if (isNativeHeap) {
    172                 stats->nativePss += pss;
    173                 stats->nativePrivateDirty += private_dirty;
    174                 stats->nativeSharedDirty += shared_dirty;
    175             } else if (isDalvikHeap) {
    176                 stats->dalvikPss += pss;
    177                 stats->dalvikPrivateDirty += private_dirty;
    178                 stats->dalvikSharedDirty += shared_dirty;
    179             } else if ( isSqliteHeap) {
    180                 // ignore
    181             } else {
    182                 stats->otherPss += pss;
    183                 stats->otherPrivateDirty += private_dirty;
    184                 stats->otherSharedDirty += shared_dirty;
    185             }
    186         }
    187     }
    188 }
    189 
    190 static void load_maps(int pid, stats_t* stats)
    191 {
    192     char tmp[128];
    193     FILE *fp;
    194 
    195     sprintf(tmp, "/proc/%d/smaps", pid);
    196     fp = fopen(tmp, "r");
    197     if (fp == 0) return;
    198 
    199     read_mapinfo(fp, stats);
    200     fclose(fp);
    201 }
    202 
    203 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
    204         jint pid, jobject object)
    205 {
    206     stats_t stats;
    207     memset(&stats, 0, sizeof(stats_t));
    208 
    209     load_maps(pid, &stats);
    210 
    211     env->SetIntField(object, dalvikPss_field, stats.dalvikPss);
    212     env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty);
    213     env->SetIntField(object, dalvikSharedDirty_field, stats.dalvikSharedDirty);
    214 
    215     env->SetIntField(object, nativePss_field, stats.nativePss);
    216     env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty);
    217     env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty);
    218 
    219     env->SetIntField(object, otherPss_field, stats.otherPss);
    220     env->SetIntField(object, otherPrivateDirty_field, stats.otherPrivateDirty);
    221     env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty);
    222 }
    223 
    224 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
    225 {
    226     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
    227 }
    228 
    229 static jint read_binder_stat(const char* stat)
    230 {
    231     FILE* fp = fopen(BINDER_STATS, "r");
    232     if (fp == NULL) {
    233         return -1;
    234     }
    235 
    236     char line[1024];
    237 
    238     char compare[128];
    239     int len = snprintf(compare, 128, "proc %d", getpid());
    240 
    241     // loop until we have the block that represents this process
    242     do {
    243         if (fgets(line, 1024, fp) == 0) {
    244             return -1;
    245         }
    246     } while (strncmp(compare, line, len));
    247 
    248     // now that we have this process, read until we find the stat that we are looking for
    249     len = snprintf(compare, 128, "  %s: ", stat);
    250 
    251     do {
    252         if (fgets(line, 1024, fp) == 0) {
    253             return -1;
    254         }
    255     } while (strncmp(compare, line, len));
    256 
    257     // we have the line, now increment the line ptr to the value
    258     char* ptr = line + len;
    259     return atoi(ptr);
    260 }
    261 
    262 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
    263 {
    264     return read_binder_stat("bcTRANSACTION");
    265 }
    266 
    267 static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
    268 {
    269     return read_binder_stat("brTRANSACTION");
    270 }
    271 
    272 // these are implemented in android_util_Binder.cpp
    273 jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
    274 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
    275 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
    276 
    277 /*
    278  * JNI registration.
    279  */
    280 
    281 static JNINativeMethod gMethods[] = {
    282     { "getNativeHeapSize",      "()J",
    283             (void*) android_os_Debug_getNativeHeapSize },
    284     { "getNativeHeapAllocatedSize", "()J",
    285             (void*) android_os_Debug_getNativeHeapAllocatedSize },
    286     { "getNativeHeapFreeSize",  "()J",
    287             (void*) android_os_Debug_getNativeHeapFreeSize },
    288     { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
    289             (void*) android_os_Debug_getDirtyPages },
    290     { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
    291             (void*) android_os_Debug_getDirtyPagesPid },
    292     { "getBinderSentTransactions", "()I",
    293             (void*) android_os_Debug_getBinderSentTransactions },
    294     { "getBinderReceivedTransactions", "()I",
    295             (void*) android_os_getBinderReceivedTransactions },
    296     { "getBinderLocalObjectCount", "()I",
    297             (void*)android_os_Debug_getLocalObjectCount },
    298     { "getBinderProxyObjectCount", "()I",
    299             (void*)android_os_Debug_getProxyObjectCount },
    300     { "getBinderDeathObjectCount", "()I",
    301             (void*)android_os_Debug_getDeathObjectCount },
    302 };
    303 
    304 int register_android_os_Debug(JNIEnv *env)
    305 {
    306     jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
    307 
    308     dalvikPss_field = env->GetFieldID(clazz, "dalvikPss", "I");
    309     dalvikPrivateDirty_field = env->GetFieldID(clazz, "dalvikPrivateDirty", "I");
    310     dalvikSharedDirty_field = env->GetFieldID(clazz, "dalvikSharedDirty", "I");
    311 
    312     nativePss_field = env->GetFieldID(clazz, "nativePss", "I");
    313     nativePrivateDirty_field = env->GetFieldID(clazz, "nativePrivateDirty", "I");
    314     nativeSharedDirty_field = env->GetFieldID(clazz, "nativeSharedDirty", "I");
    315 
    316     otherPss_field = env->GetFieldID(clazz, "otherPss", "I");
    317     otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I");
    318     otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I");
    319 
    320     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
    321 }
    322 
    323 };
    324