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