android_os_Debug.cpp revision c367d48c55e5a3fa0df14fd62889e4bb6b63cb01
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 39namespace android 40{ 41 42enum { 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 61struct stat_fields { 62 jfieldID pss_field; 63 jfieldID privateDirty_field; 64 jfieldID sharedDirty_field; 65}; 66 67struct stat_field_names { 68 const char* pss_name; 69 const char* privateDirty_name; 70 const char* sharedDirty_name; 71}; 72 73static stat_fields stat_fields[_NUM_CORE_HEAP]; 74 75static stat_field_names stat_field_names[_NUM_CORE_HEAP] = { 76 { "otherPss", "otherPrivateDirty", "otherSharedDirty" }, 77 { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" }, 78 { "nativePss", "nativePrivateDirty", "nativeSharedDirty" } 79}; 80 81jfieldID otherStats_field; 82 83struct stats_t { 84 int pss; 85 int privateDirty; 86 int sharedDirty; 87}; 88 89#define BINDER_STATS "/proc/binder/stats" 90 91static 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 101static 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 111static 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 121static 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 235static 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 248static 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 285static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) 286{ 287 android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); 288} 289 290static 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 318static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz) 319{ 320 return android_os_Debug_getPssPid(env, clazz, getpid()); 321} 322 323static 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 fclose(fp); 339 return -1; 340 } 341 } while (strncmp(compare, line, len)); 342 343 // now that we have this process, read until we find the stat that we are looking for 344 len = snprintf(compare, 128, " %s: ", stat); 345 346 do { 347 if (fgets(line, 1024, fp) == 0) { 348 fclose(fp); 349 return -1; 350 } 351 } while (strncmp(compare, line, len)); 352 353 // we have the line, now increment the line ptr to the value 354 char* ptr = line + len; 355 jint result = atoi(ptr); 356 fclose(fp); 357 return result; 358} 359 360static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) 361{ 362 return read_binder_stat("bcTRANSACTION"); 363} 364 365static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz) 366{ 367 return read_binder_stat("brTRANSACTION"); 368} 369 370// these are implemented in android_util_Binder.cpp 371jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); 372jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); 373jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); 374 375 376/* pulled out of bionic */ 377extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 378 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); 379extern "C" void free_malloc_leak_info(uint8_t* info); 380#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) 381#define BACKTRACE_SIZE 32 382 383/* 384 * This is a qsort() callback. 385 * 386 * See dumpNativeHeap() for comments about the data format and sort order. 387 */ 388static int compareHeapRecords(const void* vrec1, const void* vrec2) 389{ 390 const size_t* rec1 = (const size_t*) vrec1; 391 const size_t* rec2 = (const size_t*) vrec2; 392 size_t size1 = *rec1; 393 size_t size2 = *rec2; 394 395 if (size1 < size2) { 396 return 1; 397 } else if (size1 > size2) { 398 return -1; 399 } 400 401 intptr_t* bt1 = (intptr_t*)(rec1 + 2); 402 intptr_t* bt2 = (intptr_t*)(rec2 + 2); 403 for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) { 404 intptr_t addr1 = bt1[idx]; 405 intptr_t addr2 = bt2[idx]; 406 if (addr1 == addr2) { 407 if (addr1 == 0) 408 break; 409 continue; 410 } 411 if (addr1 < addr2) { 412 return -1; 413 } else if (addr1 > addr2) { 414 return 1; 415 } 416 } 417 418 return 0; 419} 420 421/* 422 * The get_malloc_leak_info() call returns an array of structs that 423 * look like this: 424 * 425 * size_t size 426 * size_t allocations 427 * intptr_t backtrace[32] 428 * 429 * "size" is the size of the allocation, "backtrace" is a fixed-size 430 * array of function pointers, and "allocations" is the number of 431 * allocations with the exact same size and backtrace. 432 * 433 * The entries are sorted by descending total size (i.e. size*allocations) 434 * then allocation count. For best results with "diff" we'd like to sort 435 * primarily by individual size then stack trace. Since the entries are 436 * fixed-size, and we're allowed (by the current implementation) to mangle 437 * them, we can do this in place. 438 */ 439static void dumpNativeHeap(FILE* fp) 440{ 441 uint8_t* info = NULL; 442 size_t overallSize, infoSize, totalMemory, backtraceSize; 443 444 get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, 445 &backtraceSize); 446 if (info == NULL) { 447 fprintf(fp, "Native heap dump not available. To enable, run these" 448 " commands (requires root):\n"); 449 fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n"); 450 fprintf(fp, "$ adb shell stop\n"); 451 fprintf(fp, "$ adb shell start\n"); 452 return; 453 } 454 assert(infoSize != 0); 455 assert(overallSize % infoSize == 0); 456 457 fprintf(fp, "Android Native Heap Dump v1.0\n\n"); 458 459 size_t recordCount = overallSize / infoSize; 460 fprintf(fp, "Total memory: %zu\n", totalMemory); 461 fprintf(fp, "Allocation records: %zd\n", recordCount); 462 if (backtraceSize != BACKTRACE_SIZE) { 463 fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n", 464 backtraceSize, BACKTRACE_SIZE); 465 } 466 fprintf(fp, "\n"); 467 468 /* re-sort the entries */ 469 qsort(info, recordCount, infoSize, compareHeapRecords); 470 471 /* dump the entries to the file */ 472 const uint8_t* ptr = info; 473 for (size_t idx = 0; idx < recordCount; idx++) { 474 size_t size = *(size_t*) ptr; 475 size_t allocations = *(size_t*) (ptr + sizeof(size_t)); 476 intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2); 477 478 fprintf(fp, "z %d sz %8zu num %4zu bt", 479 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0, 480 size & ~SIZE_FLAG_ZYGOTE_CHILD, 481 allocations); 482 for (size_t bt = 0; bt < backtraceSize; bt++) { 483 if (backtrace[bt] == 0) { 484 break; 485 } else { 486 fprintf(fp, " %08x", backtrace[bt]); 487 } 488 } 489 fprintf(fp, "\n"); 490 491 ptr += infoSize; 492 } 493 494 free_malloc_leak_info(info); 495 496 fprintf(fp, "MAPS\n"); 497 const char* maps = "/proc/self/maps"; 498 FILE* in = fopen(maps, "r"); 499 if (in == NULL) { 500 fprintf(fp, "Could not open %s\n", maps); 501 return; 502 } 503 char buf[BUFSIZ]; 504 while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) { 505 fwrite(buf, sizeof(char), n, fp); 506 } 507 fclose(in); 508 509 fprintf(fp, "END\n"); 510} 511 512/* 513 * Dump the native heap, writing human-readable output to the specified 514 * file descriptor. 515 */ 516static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, 517 jobject fileDescriptor) 518{ 519 if (fileDescriptor == NULL) { 520 jniThrowNullPointerException(env, "fd == null"); 521 return; 522 } 523 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); 524 if (origFd < 0) { 525 jniThrowRuntimeException(env, "Invalid file descriptor"); 526 return; 527 } 528 529 /* dup() the descriptor so we don't close the original with fclose() */ 530 int fd = dup(origFd); 531 if (fd < 0) { 532 ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); 533 jniThrowRuntimeException(env, "dup() failed"); 534 return; 535 } 536 537 FILE* fp = fdopen(fd, "w"); 538 if (fp == NULL) { 539 ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); 540 close(fd); 541 jniThrowRuntimeException(env, "fdopen() failed"); 542 return; 543 } 544 545 ALOGD("Native heap dump starting...\n"); 546 dumpNativeHeap(fp); 547 ALOGD("Native heap dump complete.\n"); 548 549 fclose(fp); 550} 551 552 553static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz, 554 jint pid, jstring fileName) 555{ 556 if (fileName == NULL) { 557 jniThrowNullPointerException(env, "file == null"); 558 return; 559 } 560 const jchar* str = env->GetStringCritical(fileName, 0); 561 String8 fileName8; 562 if (str) { 563 fileName8 = String8(str, env->GetStringLength(fileName)); 564 env->ReleaseStringCritical(fileName, str); 565 } 566 567 int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */ 568 if (fd < 0) { 569 fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno)); 570 return; 571 } 572 573 if (lseek(fd, 0, SEEK_END) < 0) { 574 fprintf(stderr, "lseek: %s\n", strerror(errno)); 575 } else { 576 dump_backtrace_to_file(pid, fd); 577 } 578 579 close(fd); 580} 581 582/* 583 * JNI registration. 584 */ 585 586static JNINativeMethod gMethods[] = { 587 { "getNativeHeapSize", "()J", 588 (void*) android_os_Debug_getNativeHeapSize }, 589 { "getNativeHeapAllocatedSize", "()J", 590 (void*) android_os_Debug_getNativeHeapAllocatedSize }, 591 { "getNativeHeapFreeSize", "()J", 592 (void*) android_os_Debug_getNativeHeapFreeSize }, 593 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", 594 (void*) android_os_Debug_getDirtyPages }, 595 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", 596 (void*) android_os_Debug_getDirtyPagesPid }, 597 { "getPss", "()J", 598 (void*) android_os_Debug_getPss }, 599 { "getPss", "(I)J", 600 (void*) android_os_Debug_getPssPid }, 601 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", 602 (void*) android_os_Debug_dumpNativeHeap }, 603 { "getBinderSentTransactions", "()I", 604 (void*) android_os_Debug_getBinderSentTransactions }, 605 { "getBinderReceivedTransactions", "()I", 606 (void*) android_os_getBinderReceivedTransactions }, 607 { "getBinderLocalObjectCount", "()I", 608 (void*)android_os_Debug_getLocalObjectCount }, 609 { "getBinderProxyObjectCount", "()I", 610 (void*)android_os_Debug_getProxyObjectCount }, 611 { "getBinderDeathObjectCount", "()I", 612 (void*)android_os_Debug_getDeathObjectCount }, 613 { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V", 614 (void*)android_os_Debug_dumpNativeBacktraceToFile }, 615}; 616 617int register_android_os_Debug(JNIEnv *env) 618{ 619 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); 620 621 // Sanity check the number of other statistics expected in Java matches here. 622 jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I"); 623 jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field); 624 int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP; 625 if (numOtherStats != expectedNumOtherStats) { 626 jniThrowExceptionFmt(env, "java/lang/RuntimeException", 627 "android.os.Debug.Meminfo.NUM_OTHER_STATS=%d expected %d", 628 numOtherStats, expectedNumOtherStats); 629 return JNI_ERR; 630 } 631 632 otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); 633 634 for (int i=0; i<_NUM_CORE_HEAP; i++) { 635 stat_fields[i].pss_field = 636 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I"); 637 stat_fields[i].privateDirty_field = 638 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I"); 639 stat_fields[i].sharedDirty_field = 640 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I"); 641 } 642 643 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); 644} 645 646}; // namespace android 647