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