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