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