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 36 namespace android 37 { 38 39 enum { 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 57 struct stat_fields { 58 jfieldID pss_field; 59 jfieldID privateDirty_field; 60 jfieldID sharedDirty_field; 61 }; 62 63 struct stat_field_names { 64 const char* pss_name; 65 const char* privateDirty_name; 66 const char* sharedDirty_name; 67 }; 68 69 static stat_fields stat_fields[_NUM_CORE_HEAP]; 70 71 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = { 72 { "otherPss", "otherPrivateDirty", "otherSharedDirty" }, 73 { "dalvikPss", "dalvikPrivateDirty", "dalvikSharedDirty" }, 74 { "nativePss", "nativePrivateDirty", "nativeSharedDirty" } 75 }; 76 77 jfieldID otherStats_field; 78 79 struct stats_t { 80 int pss; 81 int privateDirty; 82 int sharedDirty; 83 }; 84 85 #define BINDER_STATS "/proc/binder/stats" 86 87 static 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 97 static 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 107 static 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 117 static 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 227 static 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 240 static 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 277 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object) 278 { 279 android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); 280 } 281 282 static 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 310 static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz) 311 { 312 return android_os_Debug_getPssPid(env, clazz, getpid()); 313 } 314 315 static 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 348 static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) 349 { 350 return read_binder_stat("bcTRANSACTION"); 351 } 352 353 static 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 359 jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); 360 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); 361 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); 362 363 364 /* pulled out of bionic */ 365 extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 366 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); 367 extern "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 */ 376 static 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 */ 427 static 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 */ 504 static 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 545 static 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 574 int 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