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