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