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