android_os_Debug.cpp revision 0e3328fbdd3845b0e2bec364e951498eaee6b079
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 jint read_binder_stat(const char* stat) 283{ 284 FILE* fp = fopen(BINDER_STATS, "r"); 285 if (fp == NULL) { 286 return -1; 287 } 288 289 char line[1024]; 290 291 char compare[128]; 292 int len = snprintf(compare, 128, "proc %d", getpid()); 293 294 // loop until we have the block that represents this process 295 do { 296 if (fgets(line, 1024, fp) == 0) { 297 return -1; 298 } 299 } while (strncmp(compare, line, len)); 300 301 // now that we have this process, read until we find the stat that we are looking for 302 len = snprintf(compare, 128, " %s: ", stat); 303 304 do { 305 if (fgets(line, 1024, fp) == 0) { 306 return -1; 307 } 308 } while (strncmp(compare, line, len)); 309 310 // we have the line, now increment the line ptr to the value 311 char* ptr = line + len; 312 return atoi(ptr); 313} 314 315static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz) 316{ 317 return read_binder_stat("bcTRANSACTION"); 318} 319 320static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz) 321{ 322 return read_binder_stat("brTRANSACTION"); 323} 324 325// these are implemented in android_util_Binder.cpp 326jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz); 327jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz); 328jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz); 329 330 331/* pulled out of bionic */ 332extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 333 size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); 334extern "C" void free_malloc_leak_info(uint8_t* info); 335#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) 336#define BACKTRACE_SIZE 32 337 338/* 339 * This is a qsort() callback. 340 * 341 * See dumpNativeHeap() for comments about the data format and sort order. 342 */ 343static int compareHeapRecords(const void* vrec1, const void* vrec2) 344{ 345 const size_t* rec1 = (const size_t*) vrec1; 346 const size_t* rec2 = (const size_t*) vrec2; 347 size_t size1 = *rec1; 348 size_t size2 = *rec2; 349 350 if (size1 < size2) { 351 return 1; 352 } else if (size1 > size2) { 353 return -1; 354 } 355 356 intptr_t* bt1 = (intptr_t*)(rec1 + 2); 357 intptr_t* bt2 = (intptr_t*)(rec2 + 2); 358 for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) { 359 intptr_t addr1 = bt1[idx]; 360 intptr_t addr2 = bt2[idx]; 361 if (addr1 == addr2) { 362 if (addr1 == 0) 363 break; 364 continue; 365 } 366 if (addr1 < addr2) { 367 return -1; 368 } else if (addr1 > addr2) { 369 return 1; 370 } 371 } 372 373 return 0; 374} 375 376/* 377 * The get_malloc_leak_info() call returns an array of structs that 378 * look like this: 379 * 380 * size_t size 381 * size_t allocations 382 * intptr_t backtrace[32] 383 * 384 * "size" is the size of the allocation, "backtrace" is a fixed-size 385 * array of function pointers, and "allocations" is the number of 386 * allocations with the exact same size and backtrace. 387 * 388 * The entries are sorted by descending total size (i.e. size*allocations) 389 * then allocation count. For best results with "diff" we'd like to sort 390 * primarily by individual size then stack trace. Since the entries are 391 * fixed-size, and we're allowed (by the current implementation) to mangle 392 * them, we can do this in place. 393 */ 394static void dumpNativeHeap(FILE* fp) 395{ 396 uint8_t* info = NULL; 397 size_t overallSize, infoSize, totalMemory, backtraceSize; 398 399 get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, 400 &backtraceSize); 401 if (info == NULL) { 402 fprintf(fp, "Native heap dump not available. To enable, run these" 403 " commands (requires root):\n"); 404 fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n"); 405 fprintf(fp, "$ adb shell stop\n"); 406 fprintf(fp, "$ adb shell start\n"); 407 return; 408 } 409 assert(infoSize != 0); 410 assert(overallSize % infoSize == 0); 411 412 fprintf(fp, "Android Native Heap Dump v1.0\n\n"); 413 414 size_t recordCount = overallSize / infoSize; 415 fprintf(fp, "Total memory: %zu\n", totalMemory); 416 fprintf(fp, "Allocation records: %zd\n", recordCount); 417 if (backtraceSize != BACKTRACE_SIZE) { 418 fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n", 419 backtraceSize, BACKTRACE_SIZE); 420 } 421 fprintf(fp, "\n"); 422 423 /* re-sort the entries */ 424 qsort(info, recordCount, infoSize, compareHeapRecords); 425 426 /* dump the entries to the file */ 427 const uint8_t* ptr = info; 428 for (size_t idx = 0; idx < recordCount; idx++) { 429 size_t size = *(size_t*) ptr; 430 size_t allocations = *(size_t*) (ptr + sizeof(size_t)); 431 intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2); 432 433 fprintf(fp, "z %d sz %8zu num %4zu bt", 434 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0, 435 size & ~SIZE_FLAG_ZYGOTE_CHILD, 436 allocations); 437 for (size_t bt = 0; bt < backtraceSize; bt++) { 438 if (backtrace[bt] == 0) { 439 break; 440 } else { 441 fprintf(fp, " %08x", backtrace[bt]); 442 } 443 } 444 fprintf(fp, "\n"); 445 446 ptr += infoSize; 447 } 448 449 free_malloc_leak_info(info); 450 451 fprintf(fp, "MAPS\n"); 452 const char* maps = "/proc/self/maps"; 453 FILE* in = fopen(maps, "r"); 454 if (in == NULL) { 455 fprintf(fp, "Could not open %s\n", maps); 456 return; 457 } 458 char buf[BUFSIZ]; 459 while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) { 460 fwrite(buf, sizeof(char), n, fp); 461 } 462 fclose(in); 463 464 fprintf(fp, "END\n"); 465} 466 467/* 468 * Dump the native heap, writing human-readable output to the specified 469 * file descriptor. 470 */ 471static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, 472 jobject fileDescriptor) 473{ 474 if (fileDescriptor == NULL) { 475 jniThrowNullPointerException(env, NULL); 476 return; 477 } 478 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); 479 if (origFd < 0) { 480 jniThrowRuntimeException(env, "Invalid file descriptor"); 481 return; 482 } 483 484 /* dup() the descriptor so we don't close the original with fclose() */ 485 int fd = dup(origFd); 486 if (fd < 0) { 487 LOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); 488 jniThrowRuntimeException(env, "dup() failed"); 489 return; 490 } 491 492 FILE* fp = fdopen(fd, "w"); 493 if (fp == NULL) { 494 LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); 495 close(fd); 496 jniThrowRuntimeException(env, "fdopen() failed"); 497 return; 498 } 499 500 LOGD("Native heap dump starting...\n"); 501 dumpNativeHeap(fp); 502 LOGD("Native heap dump complete.\n"); 503 504 fclose(fp); 505} 506 507 508/* 509 * JNI registration. 510 */ 511 512static JNINativeMethod gMethods[] = { 513 { "getNativeHeapSize", "()J", 514 (void*) android_os_Debug_getNativeHeapSize }, 515 { "getNativeHeapAllocatedSize", "()J", 516 (void*) android_os_Debug_getNativeHeapAllocatedSize }, 517 { "getNativeHeapFreeSize", "()J", 518 (void*) android_os_Debug_getNativeHeapFreeSize }, 519 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", 520 (void*) android_os_Debug_getDirtyPages }, 521 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", 522 (void*) android_os_Debug_getDirtyPagesPid }, 523 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", 524 (void*) android_os_Debug_dumpNativeHeap }, 525 { "getBinderSentTransactions", "()I", 526 (void*) android_os_Debug_getBinderSentTransactions }, 527 { "getBinderReceivedTransactions", "()I", 528 (void*) android_os_getBinderReceivedTransactions }, 529 { "getBinderLocalObjectCount", "()I", 530 (void*)android_os_Debug_getLocalObjectCount }, 531 { "getBinderProxyObjectCount", "()I", 532 (void*)android_os_Debug_getProxyObjectCount }, 533 { "getBinderDeathObjectCount", "()I", 534 (void*)android_os_Debug_getDeathObjectCount }, 535}; 536 537int register_android_os_Debug(JNIEnv *env) 538{ 539 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); 540 541 for (int i=0; i<_NUM_CORE_HEAP; i++) { 542 stat_fields[i].pss_field = 543 env->GetFieldID(clazz, stat_field_names[i].pss_name, "I"); 544 stat_fields[i].privateDirty_field = 545 env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I"); 546 stat_fields[i].sharedDirty_field = 547 env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I"); 548 } 549 550 otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); 551 552 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); 553} 554 555}; // namespace android 556