OSDN Git Service

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