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