android_os_Debug.cpp revision 8e69257a9c7e9c1781e1f53d8856358ada38921d
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 519static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out) 520{ 521 char buffer[1024]; 522 int numFound = 0; 523 524 if (out == NULL) { 525 jniThrowNullPointerException(env, "out == null"); 526 return; 527 } 528 529 int fd = open("/proc/meminfo", O_RDONLY); 530 531 if (fd < 0) { 532 printf("Unable to open /proc/meminfo: %s\n", strerror(errno)); 533 return; 534 } 535 536 const int len = read(fd, buffer, sizeof(buffer)-1); 537 close(fd); 538 539 if (len < 0) { 540 printf("Empty /proc/meminfo"); 541 return; 542 } 543 buffer[len] = 0; 544 545 static const char* const tags[] = { 546 "MemTotal:", 547 "MemFree:", 548 "Buffers:", 549 "Cached:", 550 "Shmem:", 551 "Slab:", 552 NULL 553 }; 554 static const int tagsLen[] = { 555 9, 556 8, 557 8, 558 7, 559 6, 560 5, 561 0 562 }; 563 long mem[] = { 0, 0, 0, 0, 0, 0 }; 564 565 char* p = buffer; 566 while (*p && numFound < 6) { 567 int i = 0; 568 while (tags[i]) { 569 if (strncmp(p, tags[i], tagsLen[i]) == 0) { 570 p += tagsLen[i]; 571 while (*p == ' ') p++; 572 char* num = p; 573 while (*p >= '0' && *p <= '9') p++; 574 if (*p != 0) { 575 *p = 0; 576 p++; 577 } 578 mem[i] = atoll(num); 579 numFound++; 580 break; 581 } 582 i++; 583 } 584 while (*p && *p != '\n') { 585 p++; 586 } 587 if (*p) p++; 588 } 589 590 int maxNum = env->GetArrayLength(out); 591 jlong* outArray = env->GetLongArrayElements(out, 0); 592 if (outArray != NULL) { 593 for (int i=0; i<maxNum && tags[i]; i++) { 594 outArray[i] = mem[i]; 595 } 596 } 597 env->ReleaseLongArrayElements(out, outArray, 0); 598} 599 600static jint read_binder_stat(const char* stat) 601{ 602 FILE* fp = fopen(BINDER_STATS, "r"); 603 if (fp == NULL) { 604 return -1; 605 } 606 607 char line[1024]; 608 609 char compare[128]; 610 int len = snprintf(compare, 128, "proc %d", getpid()); 611 612 // loop until we have the block that represents this process 613 do { 614 if (fgets(line, 1024, fp) == 0) { 615 return -1; 616 } 617 } while (strncmp(compare, line, len)); 618 619 // now that we have this process, read until we find the stat that we are looking for 620 len = snprintf(compare, 128, " %s: ", stat); 621 622 do { 623 if (fgets(line, 1024, fp) == 0) { 624 return -1; 625 } 626 } while (strncmp(compare, line, len)); 627 628 // we have the line, now increment the line ptr to the value 629 char* ptr = line + len; 630 return atoi(ptr); 631} 632 633static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) 634{ 635 return read_binder_stat("bcTRANSACTION"); 636} 637 638static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz) 639{ 640 return read_binder_stat("brTRANSACTION"); 641} 642 643// these are implemented in android_util_Binder.cpp 644jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); 645jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); 646jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); 647 648 649/* pulled out of bionic */ 650extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 651 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); 652extern "C" void free_malloc_leak_info(uint8_t* info); 653#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) 654#define BACKTRACE_SIZE 32 655 656/* 657 * This is a qsort() callback. 658 * 659 * See dumpNativeHeap() for comments about the data format and sort order. 660 */ 661static int compareHeapRecords(const void* vrec1, const void* vrec2) 662{ 663 const size_t* rec1 = (const size_t*) vrec1; 664 const size_t* rec2 = (const size_t*) vrec2; 665 size_t size1 = *rec1; 666 size_t size2 = *rec2; 667 668 if (size1 < size2) { 669 return 1; 670 } else if (size1 > size2) { 671 return -1; 672 } 673 674 intptr_t* bt1 = (intptr_t*)(rec1 + 2); 675 intptr_t* bt2 = (intptr_t*)(rec2 + 2); 676 for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) { 677 intptr_t addr1 = bt1[idx]; 678 intptr_t addr2 = bt2[idx]; 679 if (addr1 == addr2) { 680 if (addr1 == 0) 681 break; 682 continue; 683 } 684 if (addr1 < addr2) { 685 return -1; 686 } else if (addr1 > addr2) { 687 return 1; 688 } 689 } 690 691 return 0; 692} 693 694/* 695 * The get_malloc_leak_info() call returns an array of structs that 696 * look like this: 697 * 698 * size_t size 699 * size_t allocations 700 * intptr_t backtrace[32] 701 * 702 * "size" is the size of the allocation, "backtrace" is a fixed-size 703 * array of function pointers, and "allocations" is the number of 704 * allocations with the exact same size and backtrace. 705 * 706 * The entries are sorted by descending total size (i.e. size*allocations) 707 * then allocation count. For best results with "diff" we'd like to sort 708 * primarily by individual size then stack trace. Since the entries are 709 * fixed-size, and we're allowed (by the current implementation) to mangle 710 * them, we can do this in place. 711 */ 712static void dumpNativeHeap(FILE* fp) 713{ 714 uint8_t* info = NULL; 715 size_t overallSize, infoSize, totalMemory, backtraceSize; 716 717 get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, 718 &backtraceSize); 719 if (info == NULL) { 720 fprintf(fp, "Native heap dump not available. To enable, run these" 721 " commands (requires root):\n"); 722 fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n"); 723 fprintf(fp, "$ adb shell stop\n"); 724 fprintf(fp, "$ adb shell start\n"); 725 return; 726 } 727 assert(infoSize != 0); 728 assert(overallSize % infoSize == 0); 729 730 fprintf(fp, "Android Native Heap Dump v1.0\n\n"); 731 732 size_t recordCount = overallSize / infoSize; 733 fprintf(fp, "Total memory: %zu\n", totalMemory); 734 fprintf(fp, "Allocation records: %zd\n", recordCount); 735 if (backtraceSize != BACKTRACE_SIZE) { 736 fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n", 737 backtraceSize, BACKTRACE_SIZE); 738 } 739 fprintf(fp, "\n"); 740 741 /* re-sort the entries */ 742 qsort(info, recordCount, infoSize, compareHeapRecords); 743 744 /* dump the entries to the file */ 745 const uint8_t* ptr = info; 746 for (size_t idx = 0; idx < recordCount; idx++) { 747 size_t size = *(size_t*) ptr; 748 size_t allocations = *(size_t*) (ptr + sizeof(size_t)); 749 intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2); 750 751 fprintf(fp, "z %d sz %8zu num %4zu bt", 752 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0, 753 size & ~SIZE_FLAG_ZYGOTE_CHILD, 754 allocations); 755 for (size_t bt = 0; bt < backtraceSize; bt++) { 756 if (backtrace[bt] == 0) { 757 break; 758 } else { 759 fprintf(fp, " %08x", backtrace[bt]); 760 } 761 } 762 fprintf(fp, "\n"); 763 764 ptr += infoSize; 765 } 766 767 free_malloc_leak_info(info); 768 769 fprintf(fp, "MAPS\n"); 770 const char* maps = "/proc/self/maps"; 771 FILE* in = fopen(maps, "r"); 772 if (in == NULL) { 773 fprintf(fp, "Could not open %s\n", maps); 774 return; 775 } 776 char buf[BUFSIZ]; 777 while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) { 778 fwrite(buf, sizeof(char), n, fp); 779 } 780 fclose(in); 781 782 fprintf(fp, "END\n"); 783} 784 785/* 786 * Dump the native heap, writing human-readable output to the specified 787 * file descriptor. 788 */ 789static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, 790 jobject fileDescriptor) 791{ 792 if (fileDescriptor == NULL) { 793 jniThrowNullPointerException(env, "fd == null"); 794 return; 795 } 796 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); 797 if (origFd < 0) { 798 jniThrowRuntimeException(env, "Invalid file descriptor"); 799 return; 800 } 801 802 /* dup() the descriptor so we don't close the original with fclose() */ 803 int fd = dup(origFd); 804 if (fd < 0) { 805 ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); 806 jniThrowRuntimeException(env, "dup() failed"); 807 return; 808 } 809 810 FILE* fp = fdopen(fd, "w"); 811 if (fp == NULL) { 812 ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); 813 close(fd); 814 jniThrowRuntimeException(env, "fdopen() failed"); 815 return; 816 } 817 818 ALOGD("Native heap dump starting...\n"); 819 dumpNativeHeap(fp); 820 ALOGD("Native heap dump complete.\n"); 821 822 fclose(fp); 823} 824 825 826static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz, 827 jint pid, jstring fileName) 828{ 829 if (fileName == NULL) { 830 jniThrowNullPointerException(env, "file == null"); 831 return; 832 } 833 const jchar* str = env->GetStringCritical(fileName, 0); 834 String8 fileName8; 835 if (str) { 836 fileName8 = String8(str, env->GetStringLength(fileName)); 837 env->ReleaseStringCritical(fileName, str); 838 } 839 840 int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */ 841 if (fd < 0) { 842 fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno)); 843 return; 844 } 845 846 if (lseek(fd, 0, SEEK_END) < 0) { 847 fprintf(stderr, "lseek: %s\n", strerror(errno)); 848 } else { 849 dump_backtrace_to_file(pid, fd); 850 } 851 852 close(fd); 853} 854 855/* 856 * JNI registration. 857 */ 858 859static JNINativeMethod gMethods[] = { 860 { "getNativeHeapSize", "()J", 861 (void*) android_os_Debug_getNativeHeapSize }, 862 { "getNativeHeapAllocatedSize", "()J", 863 (void*) android_os_Debug_getNativeHeapAllocatedSize }, 864 { "getNativeHeapFreeSize", "()J", 865 (void*) android_os_Debug_getNativeHeapFreeSize }, 866 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", 867 (void*) android_os_Debug_getDirtyPages }, 868 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", 869 (void*) android_os_Debug_getDirtyPagesPid }, 870 { "getPss", "()J", 871 (void*) android_os_Debug_getPss }, 872 { "getPss", "(I[J)J", 873 (void*) android_os_Debug_getPssPid }, 874 { "getMemInfo", "([J)V", 875 (void*) android_os_Debug_getMemInfo }, 876 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", 877 (void*) android_os_Debug_dumpNativeHeap }, 878 { "getBinderSentTransactions", "()I", 879 (void*) android_os_Debug_getBinderSentTransactions }, 880 { "getBinderReceivedTransactions", "()I", 881 (void*) android_os_getBinderReceivedTransactions }, 882 { "getBinderLocalObjectCount", "()I", 883 (void*)android_os_Debug_getLocalObjectCount }, 884 { "getBinderProxyObjectCount", "()I", 885 (void*)android_os_Debug_getProxyObjectCount }, 886 { "getBinderDeathObjectCount", "()I", 887 (void*)android_os_Debug_getDeathObjectCount }, 888 { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V", 889 (void*)android_os_Debug_dumpNativeBacktraceToFile }, 890}; 891 892int register_android_os_Debug(JNIEnv *env) 893{ 894 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); 895 896 // Sanity check the number of other statistics expected in Java matches here. 897 jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I"); 898 jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field); 899 jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I"); 900 jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field); 901 int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP; 902 if ((numOtherStats + numDvkStats) != expectedNumOtherStats) { 903 jniThrowExceptionFmt(env, "java/lang/RuntimeException", 904 "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d", 905 numOtherStats+numDvkStats, expectedNumOtherStats); 906 return JNI_ERR; 907 } 908 909 otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); 910 911 for (int i=0; i<_NUM_CORE_HEAP; i++) { 912 stat_fields[i].pss_field = 913 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I"); 914 stat_fields[i].pssSwappable_field = 915 env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I"); 916 stat_fields[i].privateDirty_field = 917 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I"); 918 stat_fields[i].sharedDirty_field = 919 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I"); 920 stat_fields[i].privateClean_field = 921 env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I"); 922 stat_fields[i].sharedClean_field = 923 env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I"); 924 } 925 926 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); 927} 928 929}; // namespace android 930