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