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 50namespace android 51{ 52 53static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) { 54 return UniqueFile(fopen(path, mode), safeFclose); 55} 56 57enum { 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 107struct 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 118struct 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 129static stat_fields stat_fields[_NUM_CORE_HEAP]; 130 131static 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 140jfieldID otherStats_field; 141jfieldID hasSwappedOutPss_field; 142 143struct 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 154enum pss_rollup_support { 155 PSS_ROLLUP_UNTRIED, 156 PSS_ROLLUP_SUPPORTED, 157 PSS_ROLLUP_UNSUPPORTED 158}; 159 160static std::atomic<pss_rollup_support> g_pss_rollup_support; 161 162#define BINDER_STATS "/proc/binder/stats" 163 164static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz) 165{ 166 struct mallinfo info = mallinfo(); 167 return (jlong) info.usmblks; 168} 169 170static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz) 171{ 172 struct mallinfo info = mallinfo(); 173 return (jlong) info.uordblks; 174} 175 176static 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 183struct 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 */ 194static 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 */ 230static 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 243static 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 474static 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 485static 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 550static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) 551{ 552 android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); 553} 554 555UniqueFile 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 582static 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 660static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz) 661{ 662 return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL); 663} 664 665static 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 701enum { 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 720static 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 748static 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 856static 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 890static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) 891{ 892 return read_binder_stat("bcTRANSACTION"); 893} 894 895static 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 901jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); 902jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); 903jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); 904 905 906/* pulled out of bionic */ 907extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 908 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); 909extern "C" void free_malloc_leak_info(uint8_t* info); 910#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) 911 912static 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 */ 919static 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 */ 970static 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 1045static 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 */ 1079static 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 */ 1096static 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 1107static 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 1125static 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 1131static 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 1137static 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 1148static 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 1187int 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