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