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