• 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 
19 #include <assert.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <malloc.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <time.h>
30 #include <unistd.h>
31 
32 #include <atomic>
33 #include <iomanip>
34 #include <string>
35 
36 #include <android-base/stringprintf.h>
37 #include <android-base/unique_fd.h>
38 #include <debuggerd/client.h>
39 #include <log/log.h>
40 #include <utils/misc.h>
41 #include <utils/String8.h>
42 
43 #include "JNIHelp.h"
44 #include "ScopedUtfChars.h"
45 #include "jni.h"
46 #include <memtrack/memtrack.h>
47 #include <memunreachable/memunreachable.h>
48 #include "android_os_Debug.h"
49 
50 namespace android
51 {
52 
MakeUniqueFile(const char * path,const char * mode)53 static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
54     return UniqueFile(fopen(path, mode), safeFclose);
55 }
56 
57 enum {
58     HEAP_UNKNOWN,
59     HEAP_DALVIK,
60     HEAP_NATIVE,
61 
62     HEAP_DALVIK_OTHER,
63     HEAP_STACK,
64     HEAP_CURSOR,
65     HEAP_ASHMEM,
66     HEAP_GL_DEV,
67     HEAP_UNKNOWN_DEV,
68     HEAP_SO,
69     HEAP_JAR,
70     HEAP_APK,
71     HEAP_TTF,
72     HEAP_DEX,
73     HEAP_OAT,
74     HEAP_ART,
75     HEAP_UNKNOWN_MAP,
76     HEAP_GRAPHICS,
77     HEAP_GL,
78     HEAP_OTHER_MEMTRACK,
79 
80     // Dalvik extra sections (heap).
81     HEAP_DALVIK_NORMAL,
82     HEAP_DALVIK_LARGE,
83     HEAP_DALVIK_ZYGOTE,
84     HEAP_DALVIK_NON_MOVING,
85 
86     // Dalvik other extra sections.
87     HEAP_DALVIK_OTHER_LINEARALLOC,
88     HEAP_DALVIK_OTHER_ACCOUNTING,
89     HEAP_DALVIK_OTHER_CODE_CACHE,
90     HEAP_DALVIK_OTHER_COMPILER_METADATA,
91     HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE,
92 
93     // Boot vdex / app dex / app vdex
94     HEAP_DEX_BOOT_VDEX,
95     HEAP_DEX_APP_DEX,
96     HEAP_DEX_APP_VDEX,
97 
98     // App art, boot art.
99     HEAP_ART_APP,
100     HEAP_ART_BOOT,
101 
102     _NUM_HEAP,
103     _NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1,
104     _NUM_CORE_HEAP = HEAP_NATIVE+1
105 };
106 
107 struct stat_fields {
108     jfieldID pss_field;
109     jfieldID pssSwappable_field;
110     jfieldID privateDirty_field;
111     jfieldID sharedDirty_field;
112     jfieldID privateClean_field;
113     jfieldID sharedClean_field;
114     jfieldID swappedOut_field;
115     jfieldID swappedOutPss_field;
116 };
117 
118 struct stat_field_names {
119     const char* pss_name;
120     const char* pssSwappable_name;
121     const char* privateDirty_name;
122     const char* sharedDirty_name;
123     const char* privateClean_name;
124     const char* sharedClean_name;
125     const char* swappedOut_name;
126     const char* swappedOutPss_name;
127 };
128 
129 static stat_fields stat_fields[_NUM_CORE_HEAP];
130 
131 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
132     { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty",
133         "otherPrivateClean", "otherSharedClean", "otherSwappedOut", "otherSwappedOutPss" },
134     { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty",
135         "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut", "dalvikSwappedOutPss" },
136     { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty",
137         "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
138 };
139 
140 jfieldID otherStats_field;
141 jfieldID hasSwappedOutPss_field;
142 
143 struct stats_t {
144     int pss;
145     int swappablePss;
146     int privateDirty;
147     int sharedDirty;
148     int privateClean;
149     int sharedClean;
150     int swappedOut;
151     int swappedOutPss;
152 };
153 
154 enum pss_rollup_support {
155   PSS_ROLLUP_UNTRIED,
156   PSS_ROLLUP_SUPPORTED,
157   PSS_ROLLUP_UNSUPPORTED
158 };
159 
160 static std::atomic<pss_rollup_support> g_pss_rollup_support;
161 
162 #define BINDER_STATS "/proc/binder/stats"
163 
android_os_Debug_getNativeHeapSize(JNIEnv * env,jobject clazz)164 static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
165 {
166     struct mallinfo info = mallinfo();
167     return (jlong) info.usmblks;
168 }
169 
android_os_Debug_getNativeHeapAllocatedSize(JNIEnv * env,jobject clazz)170 static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
171 {
172     struct mallinfo info = mallinfo();
173     return (jlong) info.uordblks;
174 }
175 
android_os_Debug_getNativeHeapFreeSize(JNIEnv * env,jobject clazz)176 static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
177 {
178     struct mallinfo info = mallinfo();
179     return (jlong) info.fordblks;
180 }
181 
182 // Container used to retrieve graphics memory pss
183 struct graphics_memory_pss
184 {
185     int graphics;
186     int gl;
187     int other;
188 };
189 
190 /*
191  * Uses libmemtrack to retrieve graphics memory that the process is using.
192  * Any graphics memory reported in /proc/pid/smaps is not included here.
193  */
read_memtrack_memory(struct memtrack_proc * p,int pid,struct graphics_memory_pss * graphics_mem)194 static int read_memtrack_memory(struct memtrack_proc* p, int pid,
195         struct graphics_memory_pss* graphics_mem)
196 {
197     int err = memtrack_proc_get(p, pid);
198     if (err != 0) {
199         ALOGW("failed to get memory consumption info: %d", err);
200         return err;
201     }
202 
203     ssize_t pss = memtrack_proc_graphics_pss(p);
204     if (pss < 0) {
205         ALOGW("failed to get graphics pss: %zd", pss);
206         return pss;
207     }
208     graphics_mem->graphics = pss / 1024;
209 
210     pss = memtrack_proc_gl_pss(p);
211     if (pss < 0) {
212         ALOGW("failed to get gl pss: %zd", pss);
213         return pss;
214     }
215     graphics_mem->gl = pss / 1024;
216 
217     pss = memtrack_proc_other_pss(p);
218     if (pss < 0) {
219         ALOGW("failed to get other pss: %zd", pss);
220         return pss;
221     }
222     graphics_mem->other = pss / 1024;
223 
224     return 0;
225 }
226 
227 /*
228  * Retrieves the graphics memory that is unaccounted for in /proc/pid/smaps.
229  */
read_memtrack_memory(int pid,struct graphics_memory_pss * graphics_mem)230 static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_mem)
231 {
232     struct memtrack_proc* p = memtrack_proc_new();
233     if (p == NULL) {
234         ALOGW("failed to create memtrack_proc");
235         return -1;
236     }
237 
238     int err = read_memtrack_memory(p, pid, graphics_mem);
239     memtrack_proc_destroy(p);
240     return err;
241 }
242 
read_mapinfo(FILE * fp,stats_t * stats,bool * foundSwapPss)243 static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
244 {
245     char line[1024];
246     int len, nameLen;
247     bool skip, done = false;
248 
249     unsigned pss = 0, swappable_pss = 0;
250     float sharing_proportion = 0.0;
251     unsigned shared_clean = 0, shared_dirty = 0;
252     unsigned private_clean = 0, private_dirty = 0;
253     unsigned swapped_out = 0, swapped_out_pss = 0;
254     bool is_swappable = false;
255     unsigned temp;
256 
257     uint64_t start;
258     uint64_t end = 0;
259     uint64_t prevEnd = 0;
260     char* name;
261     int name_pos;
262 
263     int whichHeap = HEAP_UNKNOWN;
264     int subHeap = HEAP_UNKNOWN;
265     int prevHeap = HEAP_UNKNOWN;
266 
267     *foundSwapPss = false;
268 
269     if(fgets(line, sizeof(line), fp) == 0) return;
270 
271     while (!done) {
272         prevHeap = whichHeap;
273         prevEnd = end;
274         whichHeap = HEAP_UNKNOWN;
275         subHeap = HEAP_UNKNOWN;
276         skip = false;
277         is_swappable = false;
278 
279         len = strlen(line);
280         if (len < 1) return;
281         line[--len] = 0;
282 
283         if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
284             skip = true;
285         } else {
286             while (isspace(line[name_pos])) {
287                 name_pos += 1;
288             }
289             name = line + name_pos;
290             nameLen = strlen(name);
291             // Trim the end of the line if it is " (deleted)".
292             const char* deleted_str = " (deleted)";
293             if (nameLen > (int)strlen(deleted_str) &&
294                 strcmp(name+nameLen-strlen(deleted_str), deleted_str) == 0) {
295                 nameLen -= strlen(deleted_str);
296                 name[nameLen] = '\0';
297             }
298             if ((strstr(name, "[heap]") == name)) {
299                 whichHeap = HEAP_NATIVE;
300             } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) {
301                 whichHeap = HEAP_NATIVE;
302             } else if (strncmp(name, "[stack", 6) == 0) {
303                 whichHeap = HEAP_STACK;
304             } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
305                 whichHeap = HEAP_SO;
306                 is_swappable = true;
307             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
308                 whichHeap = HEAP_JAR;
309                 is_swappable = true;
310             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
311                 whichHeap = HEAP_APK;
312                 is_swappable = true;
313             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
314                 whichHeap = HEAP_TTF;
315                 is_swappable = true;
316             } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) ||
317                        (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
318                 whichHeap = HEAP_DEX;
319                 subHeap = HEAP_DEX_APP_DEX;
320                 is_swappable = true;
321             } else if (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0) {
322                 whichHeap = HEAP_DEX;
323                 // Handle system@framework@boot* and system/framework/boot*
324                 if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
325                     subHeap = HEAP_DEX_BOOT_VDEX;
326                 } else {
327                     subHeap = HEAP_DEX_APP_VDEX;
328                 }
329                 is_swappable = true;
330             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
331                 whichHeap = HEAP_OAT;
332                 is_swappable = true;
333             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
334                 whichHeap = HEAP_ART;
335                 // Handle system@framework@boot* and system/framework/boot*
336                 if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
337                     subHeap = HEAP_ART_BOOT;
338                 } else {
339                     subHeap = HEAP_ART_APP;
340                 }
341                 is_swappable = true;
342             } else if (strncmp(name, "/dev/", 5) == 0) {
343                 if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) {
344                     whichHeap = HEAP_GL_DEV;
345                 } else if (strncmp(name, "/dev/ashmem", 11) == 0) {
346                     if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) {
347                         whichHeap = HEAP_DALVIK_OTHER;
348                         if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) {
349                             subHeap = HEAP_DALVIK_OTHER_LINEARALLOC;
350                         } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) ||
351                                    (strstr(name, "/dev/ashmem/dalvik-main space") == name)) {
352                             // This is the regular Dalvik heap.
353                             whichHeap = HEAP_DALVIK;
354                             subHeap = HEAP_DALVIK_NORMAL;
355                         } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name ||
356                                    strstr(name, "/dev/ashmem/dalvik-free list large object space")
357                                        == name) {
358                             whichHeap = HEAP_DALVIK;
359                             subHeap = HEAP_DALVIK_LARGE;
360                         } else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) {
361                             whichHeap = HEAP_DALVIK;
362                             subHeap = HEAP_DALVIK_NON_MOVING;
363                         } else if (strstr(name, "/dev/ashmem/dalvik-zygote space") == name) {
364                             whichHeap = HEAP_DALVIK;
365                             subHeap = HEAP_DALVIK_ZYGOTE;
366                         } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) {
367                             subHeap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
368                         } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name ||
369                                    strstr(name, "/dev/ashmem/dalvik-data-code-cache") == name) {
370                             subHeap = HEAP_DALVIK_OTHER_CODE_CACHE;
371                         } else if (strstr(name, "/dev/ashmem/dalvik-CompilerMetadata") == name) {
372                             subHeap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
373                         } else {
374                             subHeap = HEAP_DALVIK_OTHER_ACCOUNTING;  // Default to accounting.
375                         }
376                     } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
377                         whichHeap = HEAP_CURSOR;
378                     } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) {
379                         whichHeap = HEAP_NATIVE;
380                     } else {
381                         whichHeap = HEAP_ASHMEM;
382                     }
383                 } else {
384                     whichHeap = HEAP_UNKNOWN_DEV;
385                 }
386             } else if (strncmp(name, "[anon:", 6) == 0) {
387                 whichHeap = HEAP_UNKNOWN;
388             } else if (nameLen > 0) {
389                 whichHeap = HEAP_UNKNOWN_MAP;
390             } else if (start == prevEnd && prevHeap == HEAP_SO) {
391                 // bss section of a shared library.
392                 whichHeap = HEAP_SO;
393             }
394         }
395 
396         //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
397         //    isSqliteHeap, line);
398 
399         shared_clean = 0;
400         shared_dirty = 0;
401         private_clean = 0;
402         private_dirty = 0;
403         swapped_out = 0;
404         swapped_out_pss = 0;
405 
406         while (true) {
407             if (fgets(line, 1024, fp) == 0) {
408                 done = true;
409                 break;
410             }
411 
412             if (line[0] == 'S' && sscanf(line, "Size: %d kB", &temp) == 1) {
413                 /* size = temp; */
414             } else if (line[0] == 'R' && sscanf(line, "Rss: %d kB", &temp) == 1) {
415                 /* resident = temp; */
416             } else if (line[0] == 'P' && sscanf(line, "Pss: %d kB", &temp) == 1) {
417                 pss = temp;
418             } else if (line[0] == 'S' && sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
419                 shared_clean = temp;
420             } else if (line[0] == 'S' && sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
421                 shared_dirty = temp;
422             } else if (line[0] == 'P' && sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
423                 private_clean = temp;
424             } else if (line[0] == 'P' && sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
425                 private_dirty = temp;
426             } else if (line[0] == 'R' && sscanf(line, "Referenced: %d kB", &temp) == 1) {
427                 /* referenced = temp; */
428             } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
429                 swapped_out = temp;
430             } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) {
431                 *foundSwapPss = true;
432                 swapped_out_pss = temp;
433             } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
434                 // looks like a new mapping
435                 // example: "10000000-10001000 ---p 10000000 00:00 0"
436                 break;
437             }
438         }
439 
440         if (!skip) {
441             if (is_swappable && (pss > 0)) {
442                 sharing_proportion = 0.0;
443                 if ((shared_clean > 0) || (shared_dirty > 0)) {
444                     sharing_proportion = (pss - private_clean
445                             - private_dirty)/(shared_clean+shared_dirty);
446                 }
447                 swappable_pss = (sharing_proportion*shared_clean) + private_clean;
448             } else
449                 swappable_pss = 0;
450 
451             stats[whichHeap].pss += pss;
452             stats[whichHeap].swappablePss += swappable_pss;
453             stats[whichHeap].privateDirty += private_dirty;
454             stats[whichHeap].sharedDirty += shared_dirty;
455             stats[whichHeap].privateClean += private_clean;
456             stats[whichHeap].sharedClean += shared_clean;
457             stats[whichHeap].swappedOut += swapped_out;
458             stats[whichHeap].swappedOutPss += swapped_out_pss;
459             if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER ||
460                     whichHeap == HEAP_DEX || whichHeap == HEAP_ART) {
461                 stats[subHeap].pss += pss;
462                 stats[subHeap].swappablePss += swappable_pss;
463                 stats[subHeap].privateDirty += private_dirty;
464                 stats[subHeap].sharedDirty += shared_dirty;
465                 stats[subHeap].privateClean += private_clean;
466                 stats[subHeap].sharedClean += shared_clean;
467                 stats[subHeap].swappedOut += swapped_out;
468                 stats[subHeap].swappedOutPss += swapped_out_pss;
469             }
470         }
471     }
472 }
473 
load_maps(int pid,stats_t * stats,bool * foundSwapPss)474 static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
475 {
476     *foundSwapPss = false;
477 
478     std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
479     UniqueFile fp = MakeUniqueFile(smaps_path.c_str(), "re");
480     if (fp == nullptr) return;
481 
482     read_mapinfo(fp.get(), stats, foundSwapPss);
483 }
484 
android_os_Debug_getDirtyPagesPid(JNIEnv * env,jobject clazz,jint pid,jobject object)485 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
486         jint pid, jobject object)
487 {
488     bool foundSwapPss;
489     stats_t stats[_NUM_HEAP];
490     memset(&stats, 0, sizeof(stats));
491 
492     load_maps(pid, stats, &foundSwapPss);
493 
494     struct graphics_memory_pss graphics_mem;
495     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
496         stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
497         stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
498         stats[HEAP_GL].pss = graphics_mem.gl;
499         stats[HEAP_GL].privateDirty = graphics_mem.gl;
500         stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
501         stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
502     }
503 
504     for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
505         stats[HEAP_UNKNOWN].pss += stats[i].pss;
506         stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
507         stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
508         stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
509         stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
510         stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
511         stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
512         stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
513     }
514 
515     for (int i=0; i<_NUM_CORE_HEAP; i++) {
516         env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
517         env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
518         env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
519         env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
520         env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
521         env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
522         env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
523         env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
524     }
525 
526 
527     env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
528     jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
529 
530     jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
531     if (otherArray == NULL) {
532         return;
533     }
534 
535     int j=0;
536     for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
537         otherArray[j++] = stats[i].pss;
538         otherArray[j++] = stats[i].swappablePss;
539         otherArray[j++] = stats[i].privateDirty;
540         otherArray[j++] = stats[i].sharedDirty;
541         otherArray[j++] = stats[i].privateClean;
542         otherArray[j++] = stats[i].sharedClean;
543         otherArray[j++] = stats[i].swappedOut;
544         otherArray[j++] = stats[i].swappedOutPss;
545     }
546 
547     env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
548 }
549 
android_os_Debug_getDirtyPages(JNIEnv * env,jobject clazz,jobject object)550 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
551 {
552     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
553 }
554 
OpenSmapsOrRollup(int pid)555 UniqueFile OpenSmapsOrRollup(int pid)
556 {
557     enum pss_rollup_support rollup_support =
558             g_pss_rollup_support.load(std::memory_order_relaxed);
559     if (rollup_support != PSS_ROLLUP_UNSUPPORTED) {
560         std::string smaps_rollup_path =
561                 base::StringPrintf("/proc/%d/smaps_rollup", pid);
562         UniqueFile fp_rollup = MakeUniqueFile(smaps_rollup_path.c_str(), "re");
563         if (fp_rollup == nullptr && errno != ENOENT) {
564             return fp_rollup;  // Actual error, not just old kernel.
565         }
566         if (fp_rollup != nullptr) {
567             if (rollup_support == PSS_ROLLUP_UNTRIED) {
568                 ALOGI("using rollup pss collection");
569                 g_pss_rollup_support.store(PSS_ROLLUP_SUPPORTED,
570                                            std::memory_order_relaxed);
571             }
572             return fp_rollup;
573         }
574         g_pss_rollup_support.store(PSS_ROLLUP_UNSUPPORTED,
575                                    std::memory_order_relaxed);
576     }
577 
578     std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
579     return MakeUniqueFile(smaps_path.c_str(), "re");
580 }
581 
android_os_Debug_getPssPid(JNIEnv * env,jobject clazz,jint pid,jlongArray outUssSwapPss,jlongArray outMemtrack)582 static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
583         jlongArray outUssSwapPss, jlongArray outMemtrack)
584 {
585     char line[1024];
586     jlong pss = 0;
587     jlong swapPss = 0;
588     jlong uss = 0;
589     jlong memtrack = 0;
590 
591     struct graphics_memory_pss graphics_mem;
592     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
593         pss = uss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
594     }
595 
596     {
597         UniqueFile fp = OpenSmapsOrRollup(pid);
598 
599         if (fp != nullptr) {
600             while (true) {
601                 if (fgets(line, sizeof (line), fp.get()) == NULL) {
602                     break;
603                 }
604 
605                 if (line[0] == 'P') {
606                     if (strncmp(line, "Pss:", 4) == 0) {
607                         char* c = line + 4;
608                         while (*c != 0 && (*c < '0' || *c > '9')) {
609                             c++;
610                         }
611                         pss += atoi(c);
612                     } else if (strncmp(line, "Private_Clean:", 14) == 0
613                                 || strncmp(line, "Private_Dirty:", 14) == 0) {
614                         char* c = line + 14;
615                         while (*c != 0 && (*c < '0' || *c > '9')) {
616                             c++;
617                         }
618                         uss += atoi(c);
619                     }
620                 } else if (line[0] == 'S' && strncmp(line, "SwapPss:", 8) == 0) {
621                     char* c = line + 8;
622                     jlong lSwapPss;
623                     while (*c != 0 && (*c < '0' || *c > '9')) {
624                         c++;
625                     }
626                     lSwapPss = atoi(c);
627                     swapPss += lSwapPss;
628                     pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
629                 }
630             }
631         }
632     }
633 
634     if (outUssSwapPss != NULL) {
635         if (env->GetArrayLength(outUssSwapPss) >= 1) {
636             jlong* outUssSwapPssArray = env->GetLongArrayElements(outUssSwapPss, 0);
637             if (outUssSwapPssArray != NULL) {
638                 outUssSwapPssArray[0] = uss;
639                 if (env->GetArrayLength(outUssSwapPss) >= 2) {
640                     outUssSwapPssArray[1] = swapPss;
641                 }
642             }
643             env->ReleaseLongArrayElements(outUssSwapPss, outUssSwapPssArray, 0);
644         }
645     }
646 
647     if (outMemtrack != NULL) {
648         if (env->GetArrayLength(outMemtrack) >= 1) {
649             jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
650             if (outMemtrackArray != NULL) {
651                 outMemtrackArray[0] = memtrack;
652             }
653             env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
654         }
655     }
656 
657     return pss;
658 }
659 
android_os_Debug_getPss(JNIEnv * env,jobject clazz)660 static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
661 {
662     return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
663 }
664 
get_allocated_vmalloc_memory()665 static long get_allocated_vmalloc_memory() {
666     char line[1024];
667     // Ignored tags that don't actually consume memory (ie remappings)
668     static const char* const ignored_tags[] = {
669             "ioremap",
670             "map_lowmem",
671             "vm_map_ram",
672             NULL
673     };
674     long size, vmalloc_allocated_size = 0;
675 
676     UniqueFile fp = MakeUniqueFile("/proc/vmallocinfo", "re");
677     if (fp == nullptr) {
678         return 0;
679     }
680 
681     while (true) {
682         if (fgets(line, 1024, fp.get()) == NULL) {
683             break;
684         }
685         bool valid_line = true;
686         int i = 0;
687         while (ignored_tags[i]) {
688             if (strstr(line, ignored_tags[i]) != NULL) {
689                 valid_line = false;
690                 break;
691             }
692             i++;
693         }
694         if (valid_line && (sscanf(line, "%*x-%*x %ld", &size) == 1)) {
695             vmalloc_allocated_size += size;
696         }
697     }
698     return vmalloc_allocated_size;
699 }
700 
701 enum {
702     MEMINFO_TOTAL,
703     MEMINFO_FREE,
704     MEMINFO_BUFFERS,
705     MEMINFO_CACHED,
706     MEMINFO_SHMEM,
707     MEMINFO_SLAB,
708     MEMINFO_SLAB_RECLAIMABLE,
709     MEMINFO_SLAB_UNRECLAIMABLE,
710     MEMINFO_SWAP_TOTAL,
711     MEMINFO_SWAP_FREE,
712     MEMINFO_ZRAM_TOTAL,
713     MEMINFO_MAPPED,
714     MEMINFO_VMALLOC_USED,
715     MEMINFO_PAGE_TABLES,
716     MEMINFO_KERNEL_STACK,
717     MEMINFO_COUNT
718 };
719 
get_zram_mem_used()720 static long long get_zram_mem_used()
721 {
722 #define ZRAM_SYSFS "/sys/block/zram0/"
723     UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
724     if (mm_stat_file) {
725         long long mem_used_total = 0;
726 
727         int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
728         if (matched != 1)
729             ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
730 
731         return mem_used_total;
732     }
733 
734     UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
735     if (mem_used_total_file) {
736         long long mem_used_total = 0;
737 
738         int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
739         if (matched != 1)
740             ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
741 
742         return mem_used_total;
743     }
744 
745     return 0;
746 }
747 
android_os_Debug_getMemInfo(JNIEnv * env,jobject clazz,jlongArray out)748 static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
749 {
750     char buffer[1024];
751     size_t numFound = 0;
752 
753     if (out == NULL) {
754         jniThrowNullPointerException(env, "out == null");
755         return;
756     }
757 
758     int fd = open("/proc/meminfo", O_RDONLY);
759 
760     if (fd < 0) {
761         ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
762         return;
763     }
764 
765     int len = read(fd, buffer, sizeof(buffer)-1);
766     close(fd);
767 
768     if (len < 0) {
769         ALOGW("Empty /proc/meminfo");
770         return;
771     }
772     buffer[len] = 0;
773 
774     static const char* const tags[] = {
775             "MemTotal:",
776             "MemFree:",
777             "Buffers:",
778             "Cached:",
779             "Shmem:",
780             "Slab:",
781             "SReclaimable:",
782             "SUnreclaim:",
783             "SwapTotal:",
784             "SwapFree:",
785             "ZRam:",
786             "Mapped:",
787             "VmallocUsed:",
788             "PageTables:",
789             "KernelStack:",
790             NULL
791     };
792     static const int tagsLen[] = {
793             9,
794             8,
795             8,
796             7,
797             6,
798             5,
799             13,
800             11,
801             10,
802             9,
803             5,
804             7,
805             12,
806             11,
807             12,
808             0
809     };
810     long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
811 
812     char* p = buffer;
813     while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
814         int i = 0;
815         while (tags[i]) {
816             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
817                 p += tagsLen[i];
818                 while (*p == ' ') p++;
819                 char* num = p;
820                 while (*p >= '0' && *p <= '9') p++;
821                 if (*p != 0) {
822                     *p = 0;
823                     p++;
824                 }
825                 mem[i] = atoll(num);
826                 numFound++;
827                 break;
828             }
829             i++;
830         }
831         while (*p && *p != '\n') {
832             p++;
833         }
834         if (*p) p++;
835     }
836 
837     mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
838     // Recompute Vmalloc Used since the value in meminfo
839     // doesn't account for I/O remapping which doesn't use RAM.
840     mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
841 
842     int maxNum = env->GetArrayLength(out);
843     if (maxNum > MEMINFO_COUNT) {
844         maxNum = MEMINFO_COUNT;
845     }
846     jlong* outArray = env->GetLongArrayElements(out, 0);
847     if (outArray != NULL) {
848         for (int i=0; i<maxNum; i++) {
849             outArray[i] = mem[i];
850         }
851     }
852     env->ReleaseLongArrayElements(out, outArray, 0);
853 }
854 
855 
read_binder_stat(const char * stat)856 static jint read_binder_stat(const char* stat)
857 {
858     UniqueFile fp = MakeUniqueFile(BINDER_STATS, "re");
859     if (fp == nullptr) {
860         return -1;
861     }
862 
863     char line[1024];
864 
865     char compare[128];
866     int len = snprintf(compare, 128, "proc %d", getpid());
867 
868     // loop until we have the block that represents this process
869     do {
870         if (fgets(line, 1024, fp.get()) == 0) {
871             return -1;
872         }
873     } while (strncmp(compare, line, len));
874 
875     // now that we have this process, read until we find the stat that we are looking for
876     len = snprintf(compare, 128, "  %s: ", stat);
877 
878     do {
879         if (fgets(line, 1024, fp.get()) == 0) {
880             return -1;
881         }
882     } while (strncmp(compare, line, len));
883 
884     // we have the line, now increment the line ptr to the value
885     char* ptr = line + len;
886     jint result = atoi(ptr);
887     return result;
888 }
889 
android_os_Debug_getBinderSentTransactions(JNIEnv * env,jobject clazz)890 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
891 {
892     return read_binder_stat("bcTRANSACTION");
893 }
894 
android_os_getBinderReceivedTransactions(JNIEnv * env,jobject clazz)895 static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
896 {
897     return read_binder_stat("brTRANSACTION");
898 }
899 
900 // these are implemented in android_util_Binder.cpp
901 jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
902 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
903 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
904 
905 
906 /* pulled out of bionic */
907 extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
908     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
909 extern "C" void free_malloc_leak_info(uint8_t* info);
910 #define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
911 
912 static size_t gNumBacktraceElements;
913 
914 /*
915  * This is a qsort() callback.
916  *
917  * See dumpNativeHeap() for comments about the data format and sort order.
918  */
compareHeapRecords(const void * vrec1,const void * vrec2)919 static int compareHeapRecords(const void* vrec1, const void* vrec2)
920 {
921     const size_t* rec1 = (const size_t*) vrec1;
922     const size_t* rec2 = (const size_t*) vrec2;
923     size_t size1 = *rec1;
924     size_t size2 = *rec2;
925 
926     if (size1 < size2) {
927         return 1;
928     } else if (size1 > size2) {
929         return -1;
930     }
931 
932     uintptr_t* bt1 = (uintptr_t*)(rec1 + 2);
933     uintptr_t* bt2 = (uintptr_t*)(rec2 + 2);
934     for (size_t idx = 0; idx < gNumBacktraceElements; idx++) {
935         uintptr_t addr1 = bt1[idx];
936         uintptr_t addr2 = bt2[idx];
937         if (addr1 == addr2) {
938             if (addr1 == 0)
939                 break;
940             continue;
941         }
942         if (addr1 < addr2) {
943             return -1;
944         } else if (addr1 > addr2) {
945             return 1;
946         }
947     }
948 
949     return 0;
950 }
951 
952 /*
953  * The get_malloc_leak_info() call returns an array of structs that
954  * look like this:
955  *
956  *   size_t size
957  *   size_t allocations
958  *   intptr_t backtrace[32]
959  *
960  * "size" is the size of the allocation, "backtrace" is a fixed-size
961  * array of function pointers, and "allocations" is the number of
962  * allocations with the exact same size and backtrace.
963  *
964  * The entries are sorted by descending total size (i.e. size*allocations)
965  * then allocation count.  For best results with "diff" we'd like to sort
966  * primarily by individual size then stack trace.  Since the entries are
967  * fixed-size, and we're allowed (by the current implementation) to mangle
968  * them, we can do this in place.
969  */
dumpNativeHeap(FILE * fp)970 static void dumpNativeHeap(FILE* fp)
971 {
972     uint8_t* info = NULL;
973     size_t overallSize, infoSize, totalMemory, backtraceSize;
974 
975     get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
976         &backtraceSize);
977     if (info == NULL) {
978         fprintf(fp, "Native heap dump not available. To enable, run these"
979                     " commands (requires root):\n");
980         fprintf(fp, "# adb shell stop\n");
981         fprintf(fp, "# adb shell setprop libc.debug.malloc.options "
982                     "backtrace\n");
983         fprintf(fp, "# adb shell start\n");
984         return;
985     }
986     assert(infoSize != 0);
987     assert(overallSize % infoSize == 0);
988 
989     fprintf(fp, "Android Native Heap Dump v1.0\n\n");
990 
991     size_t recordCount = overallSize / infoSize;
992     fprintf(fp, "Total memory: %zu\n", totalMemory);
993     fprintf(fp, "Allocation records: %zd\n", recordCount);
994     fprintf(fp, "Backtrace size: %zd\n", backtraceSize);
995     fprintf(fp, "\n");
996 
997     /* re-sort the entries */
998     gNumBacktraceElements = backtraceSize;
999     qsort(info, recordCount, infoSize, compareHeapRecords);
1000 
1001     /* dump the entries to the file */
1002     const uint8_t* ptr = info;
1003     for (size_t idx = 0; idx < recordCount; idx++) {
1004         size_t size = *(size_t*) ptr;
1005         size_t allocations = *(size_t*) (ptr + sizeof(size_t));
1006         uintptr_t* backtrace = (uintptr_t*) (ptr + sizeof(size_t) * 2);
1007 
1008         fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
1009                 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
1010                 size & ~SIZE_FLAG_ZYGOTE_CHILD,
1011                 allocations);
1012         for (size_t bt = 0; bt < backtraceSize; bt++) {
1013             if (backtrace[bt] == 0) {
1014                 break;
1015             } else {
1016 #ifdef __LP64__
1017                 fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
1018 #else
1019                 fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
1020 #endif
1021             }
1022         }
1023         fprintf(fp, "\n");
1024 
1025         ptr += infoSize;
1026     }
1027 
1028     free_malloc_leak_info(info);
1029 
1030     fprintf(fp, "MAPS\n");
1031     const char* maps = "/proc/self/maps";
1032     UniqueFile in = MakeUniqueFile(maps, "re");
1033     if (in == nullptr) {
1034         fprintf(fp, "Could not open %s\n", maps);
1035         return;
1036     }
1037     char buf[BUFSIZ];
1038     while (size_t n = fread(buf, sizeof(char), BUFSIZ, in.get())) {
1039         fwrite(buf, sizeof(char), n, fp);
1040     }
1041 
1042     fprintf(fp, "END\n");
1043 }
1044 
openFile(JNIEnv * env,jobject fileDescriptor,UniqueFile & fp)1045 static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
1046 {
1047     if (fileDescriptor == NULL) {
1048         jniThrowNullPointerException(env, "fd == null");
1049         return false;
1050     }
1051     int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
1052     if (origFd < 0) {
1053         jniThrowRuntimeException(env, "Invalid file descriptor");
1054         return false;
1055     }
1056 
1057     /* dup() the descriptor so we don't close the original with fclose() */
1058     int fd = dup(origFd);
1059     if (fd < 0) {
1060         ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
1061         jniThrowRuntimeException(env, "dup() failed");
1062         return false;
1063     }
1064 
1065     fp.reset(fdopen(fd, "w"));
1066     if (fp == nullptr) {
1067         ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
1068         close(fd);
1069         jniThrowRuntimeException(env, "fdopen() failed");
1070         return false;
1071     }
1072     return true;
1073 }
1074 
1075 /*
1076  * Dump the native heap, writing human-readable output to the specified
1077  * file descriptor.
1078  */
android_os_Debug_dumpNativeHeap(JNIEnv * env,jobject,jobject fileDescriptor)1079 static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject,
1080     jobject fileDescriptor)
1081 {
1082     UniqueFile fp(nullptr, safeFclose);
1083     if (!openFile(env, fileDescriptor, fp)) {
1084         return;
1085     }
1086 
1087     ALOGD("Native heap dump starting...\n");
1088     dumpNativeHeap(fp.get());
1089     ALOGD("Native heap dump complete.\n");
1090 }
1091 
1092 /*
1093  * Dump the native malloc info, writing xml output to the specified
1094  * file descriptor.
1095  */
android_os_Debug_dumpNativeMallocInfo(JNIEnv * env,jobject,jobject fileDescriptor)1096 static void android_os_Debug_dumpNativeMallocInfo(JNIEnv* env, jobject,
1097     jobject fileDescriptor)
1098 {
1099     UniqueFile fp(nullptr, safeFclose);
1100     if (!openFile(env, fileDescriptor, fp)) {
1101         return;
1102     }
1103 
1104     malloc_info(0, fp.get());
1105 }
1106 
dumpTraces(JNIEnv * env,jint pid,jstring fileName,jint timeoutSecs,DebuggerdDumpType dumpType)1107 static bool dumpTraces(JNIEnv* env, jint pid, jstring fileName, jint timeoutSecs,
1108                        DebuggerdDumpType dumpType) {
1109     const ScopedUtfChars fileNameChars(env, fileName);
1110     if (fileNameChars.c_str() == nullptr) {
1111         return false;
1112     }
1113 
1114     android::base::unique_fd fd(open(fileNameChars.c_str(),
1115                                      O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND,
1116                                      0666));
1117     if (fd < 0) {
1118         fprintf(stderr, "Can't open %s: %s\n", fileNameChars.c_str(), strerror(errno));
1119         return false;
1120     }
1121 
1122     return (dump_backtrace_to_file_timeout(pid, dumpType, timeoutSecs, fd) == 0);
1123 }
1124 
android_os_Debug_dumpJavaBacktraceToFileTimeout(JNIEnv * env,jobject clazz,jint pid,jstring fileName,jint timeoutSecs)1125 static jboolean android_os_Debug_dumpJavaBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
1126         jint pid, jstring fileName, jint timeoutSecs) {
1127     const bool ret =  dumpTraces(env, pid, fileName, timeoutSecs, kDebuggerdJavaBacktrace);
1128     return ret ? JNI_TRUE : JNI_FALSE;
1129 }
1130 
android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv * env,jobject clazz,jint pid,jstring fileName,jint timeoutSecs)1131 static jboolean android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
1132         jint pid, jstring fileName, jint timeoutSecs) {
1133     const bool ret = dumpTraces(env, pid, fileName, timeoutSecs, kDebuggerdNativeBacktrace);
1134     return ret ? JNI_TRUE : JNI_FALSE;
1135 }
1136 
android_os_Debug_getUnreachableMemory(JNIEnv * env,jobject clazz,jint limit,jboolean contents)1137 static jstring android_os_Debug_getUnreachableMemory(JNIEnv* env, jobject clazz,
1138     jint limit, jboolean contents)
1139 {
1140     std::string s = GetUnreachableMemoryString(contents, limit);
1141     return env->NewStringUTF(s.c_str());
1142 }
1143 
1144 /*
1145  * JNI registration.
1146  */
1147 
1148 static const JNINativeMethod gMethods[] = {
1149     { "getNativeHeapSize",      "()J",
1150             (void*) android_os_Debug_getNativeHeapSize },
1151     { "getNativeHeapAllocatedSize", "()J",
1152             (void*) android_os_Debug_getNativeHeapAllocatedSize },
1153     { "getNativeHeapFreeSize",  "()J",
1154             (void*) android_os_Debug_getNativeHeapFreeSize },
1155     { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
1156             (void*) android_os_Debug_getDirtyPages },
1157     { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
1158             (void*) android_os_Debug_getDirtyPagesPid },
1159     { "getPss",                 "()J",
1160             (void*) android_os_Debug_getPss },
1161     { "getPss",                 "(I[J[J)J",
1162             (void*) android_os_Debug_getPssPid },
1163     { "getMemInfo",             "([J)V",
1164             (void*) android_os_Debug_getMemInfo },
1165     { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
1166             (void*) android_os_Debug_dumpNativeHeap },
1167     { "dumpNativeMallocInfo",   "(Ljava/io/FileDescriptor;)V",
1168             (void*) android_os_Debug_dumpNativeMallocInfo },
1169     { "getBinderSentTransactions", "()I",
1170             (void*) android_os_Debug_getBinderSentTransactions },
1171     { "getBinderReceivedTransactions", "()I",
1172             (void*) android_os_getBinderReceivedTransactions },
1173     { "getBinderLocalObjectCount", "()I",
1174             (void*)android_os_Debug_getLocalObjectCount },
1175     { "getBinderProxyObjectCount", "()I",
1176             (void*)android_os_Debug_getProxyObjectCount },
1177     { "getBinderDeathObjectCount", "()I",
1178             (void*)android_os_Debug_getDeathObjectCount },
1179     { "dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
1180             (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout },
1181     { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
1182             (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
1183     { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
1184             (void*)android_os_Debug_getUnreachableMemory },
1185 };
1186 
register_android_os_Debug(JNIEnv * env)1187 int register_android_os_Debug(JNIEnv *env)
1188 {
1189     jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
1190 
1191     // Sanity check the number of other statistics expected in Java matches here.
1192     jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
1193     jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
1194     jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
1195     jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
1196     int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
1197     if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
1198         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
1199                              "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
1200                              numOtherStats+numDvkStats, expectedNumOtherStats);
1201         return JNI_ERR;
1202     }
1203 
1204     otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
1205     hasSwappedOutPss_field = env->GetFieldID(clazz, "hasSwappedOutPss", "Z");
1206 
1207     for (int i=0; i<_NUM_CORE_HEAP; i++) {
1208         stat_fields[i].pss_field =
1209                 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
1210         stat_fields[i].pssSwappable_field =
1211                 env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
1212         stat_fields[i].privateDirty_field =
1213                 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
1214         stat_fields[i].sharedDirty_field =
1215                 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
1216         stat_fields[i].privateClean_field =
1217                 env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
1218         stat_fields[i].sharedClean_field =
1219                 env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
1220         stat_fields[i].swappedOut_field =
1221                 env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
1222         stat_fields[i].swappedOutPss_field =
1223                 env->GetFieldID(clazz, stat_field_names[i].swappedOutPss_name, "I");
1224     }
1225 
1226     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
1227 }
1228 
1229 }; // namespace android
1230