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