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