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