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