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