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