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