android_os_Debug.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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
32namespace android
33{
34
35static jfieldID dalvikPss_field;
36static jfieldID dalvikPrivateDirty_field;
37static jfieldID dalvikSharedDirty_field;
38static jfieldID nativePss_field;
39static jfieldID nativePrivateDirty_field;
40static jfieldID nativeSharedDirty_field;
41static jfieldID otherPss_field;
42static jfieldID otherPrivateDirty_field;
43static jfieldID otherSharedDirty_field;
44
45struct 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
61static 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
71static 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
81static 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
91static int read_mapinfo(FILE *fp, stats_t* stats)
92{
93    char line[1024];
94    int len;
95    int skip;
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
102    int isNativeHeap;
103    int isDalvikHeap;
104    int isSqliteHeap;
105
106again:
107    isNativeHeap = 0;
108    isDalvikHeap = 0;
109    isSqliteHeap = 0;
110    skip = 0;
111
112    if(fgets(line, 1024, fp) == 0) return 0;
113
114    len = strlen(line);
115    if (len < 1) return 0;
116    line[--len] = 0;
117
118    /* ignore guard pages */
119    if (line[18] == '-') skip = 1;
120
121    start = strtoul(line, 0, 16);
122
123    if (len >= 50) {
124        if (!strcmp(line + 49, "[heap]")) {
125            isNativeHeap = 1;
126        } else if (!strncmp(line + 49, "/dalvik-LinearAlloc", strlen("/dalvik-LinearAlloc"))) {
127            isDalvikHeap = 1;
128        } else if (!strncmp(line + 49, "/mspace/dalvik-heap", strlen("/mspace/dalvik-heap"))) {
129            isDalvikHeap = 1;
130        } else if (!strncmp(line + 49, "/dalvik-heap-bitmap/", strlen("/dalvik-heap-bitmap/"))) {
131            isDalvikHeap = 1;
132        } else if (!strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) {
133            isSqliteHeap = 1;
134        }
135    }
136
137    // TODO: This needs to be fixed to be less fragile. If the order of this file changes or a new
138    // line is add, this method will return without filling out any of the information.
139
140    if (fgets(line, 1024, fp) == 0) return 0;
141    if (sscanf(line, "Size: %d kB", &size) != 1) return 0;
142    if (fgets(line, 1024, fp) == 0) return 0;
143    if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0;
144    if (fgets(line, 1024, fp) == 0) return 0;
145    if (sscanf(line, "Pss: %d kB", &pss) != 1) return 0;
146    if (fgets(line, 1024, fp) == 0) return 0;
147    if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0;
148    if (fgets(line, 1024, fp) == 0) return 0;
149    if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0;
150    if (fgets(line, 1024, fp) == 0) return 0;
151    if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0;
152    if (fgets(line, 1024, fp) == 0) return 0;
153    if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0;
154    if (fgets(line, 1024, fp) == 0) return 0;
155    if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0;
156
157    if (skip) {
158        goto again;
159    }
160
161    if (isNativeHeap) {
162        stats->nativePss += pss;
163        stats->nativePrivateDirty += private_dirty;
164        stats->nativeSharedDirty += shared_dirty;
165    } else if (isDalvikHeap) {
166        stats->dalvikPss += pss;
167        stats->dalvikPrivateDirty += private_dirty;
168        stats->dalvikSharedDirty += shared_dirty;
169    } else if (isSqliteHeap) {
170        // ignore
171    } else {
172        stats->otherPss += pss;
173        stats->otherPrivateDirty += shared_dirty;
174        stats->otherSharedDirty += private_dirty;
175    }
176
177    return 1;
178}
179
180static void load_maps(int pid, stats_t* stats)
181{
182    char tmp[128];
183    FILE *fp;
184
185    sprintf(tmp, "/proc/%d/smaps", pid);
186    fp = fopen(tmp, "r");
187    if (fp == 0) return;
188
189    while (read_mapinfo(fp, stats) != 0) {
190        // Do nothing
191    }
192    fclose(fp);
193}
194
195static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
196{
197    stats_t stats;
198    memset(&stats, 0, sizeof(stats_t));
199
200    load_maps(getpid(), &stats);
201
202    env->SetIntField(object, dalvikPss_field, stats.dalvikPss);
203    env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty);
204    env->SetIntField(object, dalvikSharedDirty_field, stats.dalvikSharedDirty);
205
206    env->SetIntField(object, nativePss_field, stats.nativePss);
207    env->SetIntField(object, nativePrivateDirty_field, stats.nativePrivateDirty);
208    env->SetIntField(object, nativeSharedDirty_field, stats.nativeSharedDirty);
209
210    env->SetIntField(object, otherPss_field, stats.otherPss);
211    env->SetIntField(object, otherPrivateDirty_field, stats.otherPrivateDirty);
212    env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty);
213}
214
215static jint read_binder_stat(const char* stat)
216{
217    FILE* fp = fopen(BINDER_STATS, "r");
218    if (fp == NULL) {
219        return -1;
220    }
221
222    char line[1024];
223
224    char compare[128];
225    int len = snprintf(compare, 128, "proc %d", getpid());
226
227    // loop until we have the block that represents this process
228    do {
229        if (fgets(line, 1024, fp) == 0) {
230            return -1;
231        }
232    } while (strncmp(compare, line, len));
233
234    // now that we have this process, read until we find the stat that we are looking for
235    len = snprintf(compare, 128, "  %s: ", stat);
236
237    do {
238        if (fgets(line, 1024, fp) == 0) {
239            return -1;
240        }
241    } while (strncmp(compare, line, len));
242
243    // we have the line, now increment the line ptr to the value
244    char* ptr = line + len;
245    return atoi(ptr);
246}
247
248static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
249{
250    return read_binder_stat("bcTRANSACTION");
251}
252
253static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
254{
255    return read_binder_stat("brTRANSACTION");
256}
257
258// these are implemented in android_util_Binder.cpp
259jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
260jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
261jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
262
263/*
264 * JNI registration.
265 */
266
267static JNINativeMethod gMethods[] = {
268    { "getNativeHeapSize",      "()J",
269            (void*) android_os_Debug_getNativeHeapSize },
270    { "getNativeHeapAllocatedSize", "()J",
271            (void*) android_os_Debug_getNativeHeapAllocatedSize },
272    { "getNativeHeapFreeSize",  "()J",
273            (void*) android_os_Debug_getNativeHeapFreeSize },
274    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
275            (void*) android_os_Debug_getDirtyPages },
276    { "getBinderSentTransactions", "()I",
277            (void*) android_os_Debug_getBinderSentTransactions },
278    { "getBinderReceivedTransactions", "()I",
279            (void*) android_os_getBinderReceivedTransactions },
280    { "getBinderLocalObjectCount", "()I",
281            (void*)android_os_Debug_getLocalObjectCount },
282    { "getBinderProxyObjectCount", "()I",
283            (void*)android_os_Debug_getProxyObjectCount },
284    { "getBinderDeathObjectCount", "()I",
285            (void*)android_os_Debug_getDeathObjectCount },
286};
287
288int register_android_os_Debug(JNIEnv *env)
289{
290    jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
291
292    dalvikPss_field = env->GetFieldID(clazz, "dalvikPss", "I");
293    dalvikPrivateDirty_field = env->GetFieldID(clazz, "dalvikPrivateDirty", "I");
294    dalvikSharedDirty_field = env->GetFieldID(clazz, "dalvikSharedDirty", "I");
295
296    nativePss_field = env->GetFieldID(clazz, "nativePss", "I");
297    nativePrivateDirty_field = env->GetFieldID(clazz, "nativePrivateDirty", "I");
298    nativeSharedDirty_field = env->GetFieldID(clazz, "nativeSharedDirty", "I");
299
300    otherPss_field = env->GetFieldID(clazz, "otherPss", "I");
301    otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I");
302    otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I");
303
304    return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
305}
306
307};
308