android_os_Debug.cpp revision 393b84c137fe52f42205cef7e63b7a04b4bfd789
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 free_malloc_leak_info(info); 401 402 fprintf(fp, "MAPS\n"); 403 const char* maps = "/proc/self/maps"; 404 FILE* in = fopen(maps, "r"); 405 if (in == NULL) { 406 fprintf(fp, "Could not open %s\n", maps); 407 return; 408 } 409 char buf[BUFSIZ]; 410 while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) { 411 fwrite(buf, sizeof(char), n, fp); 412 } 413 fclose(in); 414 415 fprintf(fp, "END\n"); 416} 417#endif /*HAVE_ANDROID_OS*/ 418 419/* 420 * Dump the native heap, writing human-readable output to the specified 421 * file descriptor. 422 */ 423static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, 424 jobject fileDescriptor) 425{ 426 if (fileDescriptor == NULL) { 427 jniThrowNullPointerException(env, NULL); 428 return; 429 } 430 int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); 431 if (origFd < 0) { 432 jniThrowRuntimeException(env, "Invalid file descriptor"); 433 return; 434 } 435 436 /* dup() the descriptor so we don't close the original with fclose() */ 437 int fd = dup(origFd); 438 if (fd < 0) { 439 LOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); 440 jniThrowRuntimeException(env, "dup() failed"); 441 return; 442 } 443 444 FILE* fp = fdopen(fd, "w"); 445 if (fp == NULL) { 446 LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); 447 close(fd); 448 jniThrowRuntimeException(env, "fdopen() failed"); 449 return; 450 } 451 452#ifdef HAVE_ANDROID_OS 453 LOGD("Native heap dump starting...\n"); 454 dumpNativeHeap(fp); 455 LOGD("Native heap dump complete.\n"); 456#else 457 fprintf(fp, "Native heap dump not available on this platform\n"); 458#endif 459 460 fclose(fp); 461} 462 463 464/* 465 * JNI registration. 466 */ 467 468static JNINativeMethod gMethods[] = { 469 { "getNativeHeapSize", "()J", 470 (void*) android_os_Debug_getNativeHeapSize }, 471 { "getNativeHeapAllocatedSize", "()J", 472 (void*) android_os_Debug_getNativeHeapAllocatedSize }, 473 { "getNativeHeapFreeSize", "()J", 474 (void*) android_os_Debug_getNativeHeapFreeSize }, 475 { "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V", 476 (void*) android_os_Debug_getDirtyPages }, 477 { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V", 478 (void*) android_os_Debug_getDirtyPagesPid }, 479 { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", 480 (void*) android_os_Debug_dumpNativeHeap }, 481 { "getBinderSentTransactions", "()I", 482 (void*) android_os_Debug_getBinderSentTransactions }, 483 { "getBinderReceivedTransactions", "()I", 484 (void*) android_os_getBinderReceivedTransactions }, 485 { "getBinderLocalObjectCount", "()I", 486 (void*)android_os_Debug_getLocalObjectCount }, 487 { "getBinderProxyObjectCount", "()I", 488 (void*)android_os_Debug_getProxyObjectCount }, 489 { "getBinderDeathObjectCount", "()I", 490 (void*)android_os_Debug_getDeathObjectCount }, 491}; 492 493int register_android_os_Debug(JNIEnv *env) 494{ 495 jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); 496 497 dalvikPss_field = env->GetFieldID(clazz, "dalvikPss", "I"); 498 dalvikPrivateDirty_field = env->GetFieldID(clazz, "dalvikPrivateDirty", "I"); 499 dalvikSharedDirty_field = env->GetFieldID(clazz, "dalvikSharedDirty", "I"); 500 501 nativePss_field = env->GetFieldID(clazz, "nativePss", "I"); 502 nativePrivateDirty_field = env->GetFieldID(clazz, "nativePrivateDirty", "I"); 503 nativeSharedDirty_field = env->GetFieldID(clazz, "nativeSharedDirty", "I"); 504 505 otherPss_field = env->GetFieldID(clazz, "otherPss", "I"); 506 otherPrivateDirty_field = env->GetFieldID(clazz, "otherPrivateDirty", "I"); 507 otherSharedDirty_field = env->GetFieldID(clazz, "otherSharedDirty", "I"); 508 509 return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); 510} 511 512}; // namespace android 513