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