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