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