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 24 #include <fcntl.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <time.h> 30 #include <sys/time.h> 31 #include <errno.h> 32 #include <assert.h> 33 #include <ctype.h> 34 35 #ifdef HAVE_MALLOC_H 36 #include <malloc.h> 37 #endif 38 39 namespace android 40 { 41 42 enum { 43 HEAP_UNKNOWN, 44 HEAP_DALVIK, 45 HEAP_NATIVE, 46 HEAP_STACK, 47 HEAP_CURSOR, 48 HEAP_ASHMEM, 49 HEAP_UNKNOWN_DEV, 50 HEAP_SO, 51 HEAP_JAR, 52 HEAP_APK, 53 HEAP_TTF, 54 HEAP_DEX, 55 HEAP_UNKNOWN_MAP, 56 57 _NUM_HEAP, 58 _NUM_CORE_HEAP = HEAP_NATIVE+1 59 }; 60 61 struct stat_fields { 62 jfieldID pss_field; 63 jfieldID privateDirty_field; 64 jfieldID sharedDirty_field; 65 }; 66 67 struct stat_field_names { 68 const char* pss_name; 69 const char* privateDirty_name; 70 const char* sharedDirty_name; 71 }; 72 73 static stat_fields stat_fields[_NUM_CORE_HEAP]; 74 75 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = { 76 { "otherPss", "otherPrivateDirty", "otherSharedDirty" }, 77 { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" }, 78 { "nativePss", "nativePrivateDirty", "nativeSharedDirty" } 79 }; 80 81 jfieldID otherStats_field; 82 83 struct stats_t { 84 int pss; 85 int privateDirty; 86 int sharedDirty; 87 }; 88 89 #define BINDER_STATS "/proc/binder/stats" 90 91 static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz) 92 { 93 #ifdef HAVE_MALLOC_H 94 struct mallinfo info = mallinfo(); 95 return (jlong) info.usmblks; 96 #else 97 return -1; 98 #endif 99 } 100 101 static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz) 102 { 103 #ifdef HAVE_MALLOC_H 104 struct mallinfo info = mallinfo(); 105 return (jlong) info.uordblks; 106 #else 107 return -1; 108 #endif 109 } 110 111 static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz) 112 { 113 #ifdef HAVE_MALLOC_H 114 struct mallinfo info = mallinfo(); 115 return (jlong) info.fordblks; 116 #else 117 return -1; 118 #endif 119 } 120 121 static void read_mapinfo(FILE *fp, stats_t* stats) 122 { 123 char line[1024]; 124 int len, nameLen; 125 bool skip, done = false; 126 127 unsigned size = 0, resident = 0, pss = 0; 128 unsigned shared_clean = 0, shared_dirty = 0; 129 unsigned private_clean = 0, private_dirty = 0; 130 unsigned referenced = 0; 131 unsigned temp; 132 133 unsigned long int start; 134 unsigned long int end = 0; 135 unsigned long int prevEnd = 0; 136 char* name; 137 int name_pos; 138 139 int whichHeap = HEAP_UNKNOWN; 140 int prevHeap = HEAP_UNKNOWN; 141 142 if(fgets(line, sizeof(line), fp) == 0) return; 143 144 while (!done) { 145 prevHeap = whichHeap; 146 prevEnd = end; 147 whichHeap = HEAP_UNKNOWN; 148 skip = false; 149 150 len = strlen(line); 151 if (len < 1) return; 152 line[--len] = 0; 153 154 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) { 155 skip = true; 156 } else { 157 while (isspace(line[name_pos])) { 158 name_pos += 1; 159 } 160 name = line + name_pos; 161 nameLen = strlen(name); 162 163 if ((strstr(name, "[heap]") == name) || 164 (strstr(name, "/dev/ashmem/libc malloc") == name)) { 165 whichHeap = HEAP_NATIVE; 166 } else if (strstr(name, "/dev/ashmem/dalvik-") == name) { 167 whichHeap = HEAP_DALVIK; 168 } else if (strstr(name, "[stack") == name) { 169 whichHeap = HEAP_STACK; 170 } else if (strstr(name, "/dev/ashmem/CursorWindow") == name) { 171 whichHeap = HEAP_CURSOR; 172 } else if (strstr(name, "/dev/ashmem/") == name) { 173 whichHeap = HEAP_ASHMEM; 174 } else if (strstr(name, "/dev/") == name) { 175 whichHeap = HEAP_UNKNOWN_DEV; 176 } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) { 177 whichHeap = HEAP_SO; 178 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) { 179 whichHeap = HEAP_JAR; 180 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) { 181 whichHeap = HEAP_APK; 182 } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) { 183 whichHeap = HEAP_TTF; 184 } else if ((nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) || 185 (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) { 186 whichHeap = HEAP_DEX; 187 } else if (nameLen > 0) { 188 whichHeap = HEAP_UNKNOWN_MAP; 189 } else if (start == prevEnd && prevHeap == HEAP_SO) { 190 // bss section of a shared library. 191 whichHeap = HEAP_SO; 192 } 193 } 194 195 //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap, 196 // isSqliteHeap, line); 197 198 while (true) { 199 if (fgets(line, 1024, fp) == 0) { 200 done = true; 201 break; 202 } 203 204 if (sscanf(line, "Size: %d kB", &temp) == 1) { 205 size = temp; 206 } else if (sscanf(line, "Rss: %d kB", &temp) == 1) { 207 resident = temp; 208 } else if (sscanf(line, "Pss: %d kB", &temp) == 1) { 209 pss = temp; 210 } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) { 211 shared_clean = temp; 212 } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) { 213 shared_dirty = temp; 214 } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) { 215 private_clean = temp; 216 } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) { 217 private_dirty = temp; 218 } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) { 219 referenced = temp; 220 } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') { 221 // looks like a new mapping 222 // example: "10000000-10001000 ---p 10000000 00:00 0" 223 break; 224 } 225 } 226 227 if (!skip) { 228 stats[whichHeap].pss += pss; 229 stats[whichHeap].privateDirty += private_dirty; 230 stats[whichHeap].sharedDirty += shared_dirty; 231 } 232 } 233 } 234 235 static void load_maps(int pid, stats_t* stats) 236 { 237 char tmp[128]; 238 FILE *fp; 239 240 sprintf(tmp, "/proc/%d/smaps", pid); 241 fp = fopen(tmp, "r"); 242 if (fp == 0) return; 243 244 read_mapinfo(fp, stats); 245 fclose(fp); 246 } 247 248 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, 249 jint pid, jobject object) 250 { 251 stats_t stats[_NUM_HEAP]; 252 memset(&stats, 0, sizeof(stats)); 253 254 load_maps(pid, stats); 255 256 for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) { 257 stats[HEAP_UNKNOWN].pss += stats[i].pss; 258 stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty; 259 stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty; 260 } 261 262 for (int i=0; i<_NUM_CORE_HEAP; i++) { 263 env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss); 264 env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty); 265 env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty); 266 } 267 268 jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field); 269 270 jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0); 271 if (otherArray == NULL) { 272 return; 273 } 274 275 int j=0; 276 for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) { 277 otherArray[j++] = stats[i].pss; 278 otherArray[j++] = stats[i].privateDirty; 279 otherArray[j++] = stats[i].sharedDirty; 280 } 281 282 env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0); 283 } 284 285 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) 286 { 287 android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); 288 } 289 290 static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid) 291 { 292 char line[1024]; 293 jlong pss = 0; 294 unsigned temp; 295 296 char tmp[128]; 297 FILE *fp; 298 299 sprintf(tmp, "/proc/%d/smaps", pid); 300 fp = fopen(tmp, "r"); 301 if (fp == 0) return 0; 302 303 while (true) { 304 if (fgets(line, 1024, fp) == 0) { 305 break; 306 } 307 308 if (sscanf(line, "Pss: %d kB", &temp) == 1) { 309 pss += temp; 310 } 311 } 312 313 fclose(fp); 314 315 return pss; 316 } 317 318 static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz) 319 { 320 return android_os_Debug_getPssPid(env, clazz, getpid()); 321 } 322 323 static jint read_binder_stat(const char* stat) 324 { 325 FILE* fp = fopen(BINDER_STATS, "r"); 326 if (fp == NULL) { 327 return -1; 328 } 329 330 char line[1024]; 331 332 char compare[128]; 333 int len = snprintf(compare, 128, "proc %d", getpid()); 334 335 // loop until we have the block that represents this process 336 do { 337 if (fgets(line, 1024, fp) == 0) { 338 return -1; 339 } 340 } while (strncmp(compare, line, len)); 341 342 // now that we have this process, read until we find the stat that we are looking for 343 len = snprintf(compare, 128, " %s: ", stat); 344 345 do { 346 if (fgets(line, 1024, fp) == 0) { 347 return -1; 348 } 349 } while (strncmp(compare, line, len)); 350 351 // we have the line, now increment the line ptr to the value 352 char* ptr = line + len; 353 return atoi(ptr); 354 } 355 356 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) 357 { 358 return read_binder_stat("bcTRANSACTION"); 359 } 360 361 static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz) 362 { 363 return read_binder_stat("brTRANSACTION"); 364 } 365 366 // these are implemented in android_util_Binder.cpp 367 jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); 368 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); 369 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); 370 371 372 /* pulled out of bionic */ 373 extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 374 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); 375 extern "C" void free_malloc_leak_info(uint8_t* info); 376 #define SIZE_FLAG_ZYGOTE_CHILD (1<<31) 377 #define BACKTRACE_SIZE 32 378 379 /* 380 * This is a qsort() callback. 381 * 382 * See dumpNativeHeap() for comments about the data format and sort order. 383 */ 384 static int compareHeapRecords(const void* vrec1, const void* vrec2) 385 { 386 const size_t* rec1 = (const size_t*) vrec1; 387 const size_t* rec2 = (const size_t*) vrec2; 388 size_t size1 = *rec1; 389 size_t size2 = *rec2; 390 391 if (size1 < size2) { 392 return 1; 393 } else if (size1 > size2) { 394 return -1; 395 } 396 397 intptr_t* bt1 = (intptr_t*)(rec1 + 2); 398 intptr_t* bt2 = (intptr_t*)(rec2 + 2); 399 for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) { 400 intptr_t addr1 = bt1[idx]; 401 intptr_t addr2 = bt2[idx]; 402 if (addr1 == addr2) { 403 if (addr1 == 0) 404 break; 405 continue; 406 } 407 if (addr1 < addr2) { 408 return -1; 409 } else if (addr1 > addr2) { 410 return 1; 411 } 412 } 413 414 return 0; 415 } 416 417 /* 418 * The get_malloc_leak_info() call returns an array of structs that 419 * look like this: 420 * 421 * size_t size 422 * size_t allocations 423 * intptr_t backtrace[32] 424 * 425 * "size" is the size of the allocation, "backtrace" is a fixed-size 426 * array of function pointers, and "allocations" is the number of 427 * allocations with the exact same size and backtrace. 428 * 429 * The entries are sorted by descending total size (i.e. size*allocations) 430 * then allocation count. For best results with "diff" we'd like to sort 431 * primarily by individual size then stack trace. Since the entries are 432 * fixed-size, and we're allowed (by the current implementation) to mangle 433 * them, we can do this in place. 434 */ 435 static void dumpNativeHeap(FILE* fp) 436 { 437 uint8_t* info = NULL; 438 size_t overallSize, infoSize, totalMemory, backtraceSize; 439 440 get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, 441 &backtraceSize); 442 if (info == NULL) { 443 fprintf(fp, "Native heap dump not available. To enable, run these" 444 " commands (requires root):\n"); 445 fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n"); 446 fprintf(fp, "$ adb shell stop\n"); 447 fprintf(fp, "$ adb shell start\n"); 448 return; 449 } 450 assert(infoSize != 0); 451 assert(overallSize % infoSize == 0); 452 453 fprintf(fp, "Android Native Heap Dump v1.0\n\n"); 454 455 size_t recordCount = overallSize / infoSize; 456 fprintf(fp, "Total memory: %zu\n", totalMemory); 457 fprintf(fp, "Allocation records: %zd\n", recordCount); 458 if (backtraceSize != BACKTRACE_SIZE) { 459 fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n", 460 backtraceSize, BACKTRACE_SIZE); 461 } 462 fprintf(fp, "\n"); 463 464 /* re-sort the entries */ 465 qsort(info, recordCount, infoSize, compareHeapRecords); 466 467 /* dump the entries to the file */ 468 const uint8_t* ptr = info; 469 for (size_t idx = 0; idx < recordCount; idx++) { 470 size_t size = *(size_t*) ptr; 471 size_t allocations = *(size_t*) (ptr + sizeof(size_t)); 472 intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2); 473 474 fprintf(fp, "z %d sz %8zu num %4zu bt", 475 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0, 476 size & ~SIZE_FLAG_ZYGOTE_CHILD, 477 allocations); 478 for (size_t bt = 0; bt < backtraceSize; bt++) { 479 if (backtrace[bt] == 0) { 480 break; 481 } else { 482 fprintf(fp, " %08x", backtrace[bt]); 483 } 484 } 485 fprintf(fp, "\n"); 486 487 ptr += infoSize; 488 } 489 490 free_malloc_leak_info(info); 491 492 fprintf(fp, "MAPS\n"); 493 const char* maps = "/proc/self/maps"; 494 FILE* in = fopen(maps, "r"); 495 if (in == NULL) { 496 fprintf(fp, "Could not open %s\n", maps); 497 return; 498 } 499 char buf[BUFSIZ]; 500 while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) { 501 fwrite(buf, sizeof(char), n, fp); 502 } 503 fclose(in); 504 505 fprintf(fp, "END\n"); 506 } 507 508 /* 509 * Dump the native heap, writing human-readable output to the specified 510 * file descriptor. 511 */ 512 static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, 513 jobject fileDescriptor) 514 { 515 if (fileDescriptor == NULL) { 516 jniThrowNullPointerException(env, "fd == null"); 517 return; 518 } 519 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); 520 if (origFd < 0) { 521 jniThrowRuntimeException(env, "Invalid file descriptor"); 522 return; 523 } 524 525 /* dup() the descriptor so we don't close the original with fclose() */ 526 int fd = dup(origFd); 527 if (fd < 0) { 528 ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); 529 jniThrowRuntimeException(env, "dup() failed"); 530 return; 531 } 532 533 FILE* fp = fdopen(fd, "w"); 534 if (fp == NULL) { 535 ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); 536 close(fd); 537 jniThrowRuntimeException(env, "fdopen() failed"); 538 return; 539 } 540 541 ALOGD("Native heap dump starting...\n"); 542 dumpNativeHeap(fp); 543 ALOGD("Native heap dump complete.\n"); 544 545 fclose(fp); 546 } 547 548 549 static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz, 550 jint pid, jstring fileName) 551 { 552 if (fileName == NULL) { 553 jniThrowNullPointerException(env, "file == null"); 554 return; 555 } 556 const jchar* str = env->GetStringCritical(fileName, 0); 557 String8 fileName8; 558 if (str) { 559 fileName8 = String8(str, env->GetStringLength(fileName)); 560 env->ReleaseStringCritical(fileName, str); 561 } 562 563 int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */ 564 if (fd < 0) { 565 fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno)); 566 return; 567 } 568 569 if (lseek(fd, 0, SEEK_END) < 0) { 570 fprintf(stderr, "lseek: %s\n", strerror(errno)); 571 } else { 572 dump_backtrace_to_file(pid, fd); 573 } 574 575 close(fd); 576 } 577 578 /* 579 * JNI registration. 580 */ 581 582 static JNINativeMethod gMethods[] = { 583 { "getNativeHeapSize", "()J", 584 (void*) android_os_Debug_getNativeHeapSize }, 585 { "getNativeHeapAllocatedSize", "()J", 586 (void*) android_os_Debug_getNativeHeapAllocatedSize }, 587 { "getNativeHeapFreeSize", "()J", 588 (void*) android_os_Debug_getNativeHeapFreeSize }, 589 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", 590 (void*) android_os_Debug_getDirtyPages }, 591 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", 592 (void*) android_os_Debug_getDirtyPagesPid }, 593 { "getPss", "()J", 594 (void*) android_os_Debug_getPss }, 595 { "getPss", "(I)J", 596 (void*) android_os_Debug_getPssPid }, 597 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", 598 (void*) android_os_Debug_dumpNativeHeap }, 599 { "getBinderSentTransactions", "()I", 600 (void*) android_os_Debug_getBinderSentTransactions }, 601 { "getBinderReceivedTransactions", "()I", 602 (void*) android_os_getBinderReceivedTransactions }, 603 { "getBinderLocalObjectCount", "()I", 604 (void*)android_os_Debug_getLocalObjectCount }, 605 { "getBinderProxyObjectCount", "()I", 606 (void*)android_os_Debug_getProxyObjectCount }, 607 { "getBinderDeathObjectCount", "()I", 608 (void*)android_os_Debug_getDeathObjectCount }, 609 { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V", 610 (void*)android_os_Debug_dumpNativeBacktraceToFile }, 611 }; 612 613 int register_android_os_Debug(JNIEnv *env) 614 { 615 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); 616 617 // Sanity check the number of other statistics expected in Java matches here. 618 jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I"); 619 jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field); 620 int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP; 621 if (numOtherStats != expectedNumOtherStats) { 622 jniThrowExceptionFmt(env, "java/lang/RuntimeException", 623 "android.os.Debug.Meminfo.NUM_OTHER_STATS=%d expected %d", 624 numOtherStats, expectedNumOtherStats); 625 return JNI_ERR; 626 } 627 628 otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); 629 630 for (int i=0; i<_NUM_CORE_HEAP; i++) { 631 stat_fields[i].pss_field = 632 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I"); 633 stat_fields[i].privateDirty_field = 634 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I"); 635 stat_fields[i].sharedDirty_field = 636 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I"); 637 } 638 639 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); 640 } 641 642 }; // namespace android 643