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