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