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