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
android_os_Debug_getNativeHeapSize(JNIEnv * env,jobject clazz)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
android_os_Debug_getNativeHeapAllocatedSize(JNIEnv * env,jobject clazz)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
android_os_Debug_getNativeHeapFreeSize(JNIEnv * env,jobject clazz)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
read_mapinfo(FILE * fp,stats_t * stats)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
load_maps(int pid,stats_t * stats)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
android_os_Debug_getDirtyPagesPid(JNIEnv * env,jobject clazz,jint pid,jobject object)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
android_os_Debug_getDirtyPages(JNIEnv * env,jobject clazz,jobject object)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
read_binder_stat(const char * stat)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
android_os_Debug_getBinderSentTransactions(JNIEnv * env,jobject clazz)262 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
263 {
264 return read_binder_stat("bcTRANSACTION");
265 }
266
android_os_getBinderReceivedTransactions(JNIEnv * env,jobject clazz)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
register_android_os_Debug(JNIEnv * env)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