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