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