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