android_os_Debug.cpp revision 87eac99a21772ae56018cb81db6966557b459554
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/String8.h>
21#include "utils/misc.h"
22#include "cutils/debugger.h"
23
24#include <cutils/log.h>
25#include <fcntl.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30#include <time.h>
31#include <sys/time.h>
32#include <errno.h>
33#include <assert.h>
34#include <ctype.h>
35
36#ifdef HAVE_MALLOC_H
37#include <malloc.h>
38#endif
39
40namespace android
41{
42
43enum {
44    HEAP_UNKNOWN,
45    HEAP_DALVIK,
46    HEAP_NATIVE,
47    HEAP_DALVIK_OTHER,
48    HEAP_STACK,
49    HEAP_CURSOR,
50    HEAP_ASHMEM,
51    HEAP_UNKNOWN_DEV,
52    HEAP_SO,
53    HEAP_JAR,
54    HEAP_APK,
55    HEAP_TTF,
56    HEAP_DEX,
57    HEAP_OAT,
58    HEAP_ART,
59    HEAP_UNKNOWN_MAP,
60    HEAP_GPU,
61
62    HEAP_DALVIK_NORMAL,
63    HEAP_DALVIK_LARGE,
64    HEAP_DALVIK_LINEARALLOC,
65    HEAP_DALVIK_ACCOUNTING,
66    HEAP_DALVIK_CODE_CACHE,
67
68    _NUM_HEAP,
69    _NUM_EXCLUSIVE_HEAP = HEAP_GPU+1,
70    _NUM_CORE_HEAP = HEAP_NATIVE+1
71};
72
73struct stat_fields {
74    jfieldID pss_field;
75    jfieldID pssSwappable_field;
76    jfieldID privateDirty_field;
77    jfieldID sharedDirty_field;
78    jfieldID privateClean_field;
79    jfieldID sharedClean_field;
80};
81
82struct stat_field_names {
83    const char* pss_name;
84    const char* pssSwappable_name;
85    const char* privateDirty_name;
86    const char* sharedDirty_name;
87    const char* privateClean_name;
88    const char* sharedClean_name;
89};
90
91static stat_fields stat_fields[_NUM_CORE_HEAP];
92
93static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
94    { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty", "otherPrivateClean", "otherSharedClean" },
95    { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty", "dalvikPrivateClean", "dalvikSharedClean" },
96    { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty", "nativePrivateClean", "nativeSharedClean" }
97};
98
99jfieldID otherStats_field;
100
101struct stats_t {
102    int pss;
103    int swappablePss;
104    int privateDirty;
105    int sharedDirty;
106    int privateClean;
107    int sharedClean;
108};
109
110#define BINDER_STATS "/proc/binder/stats"
111
112static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
113{
114#ifdef HAVE_MALLOC_H
115    struct mallinfo info = mallinfo();
116    return (jlong) info.usmblks;
117#else
118    return -1;
119#endif
120}
121
122static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
123{
124#ifdef HAVE_MALLOC_H
125    struct mallinfo info = mallinfo();
126    return (jlong) info.uordblks;
127#else
128    return -1;
129#endif
130}
131
132static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
133{
134#ifdef HAVE_MALLOC_H
135    struct mallinfo info = mallinfo();
136    return (jlong) info.fordblks;
137#else
138    return -1;
139#endif
140}
141
142// XXX Qualcom-specific!
143static jlong read_gpu_mem(int pid)
144{
145    char line[1024];
146    jlong uss = 0;
147    unsigned temp;
148
149    char tmp[128];
150    FILE *fp;
151
152    sprintf(tmp, "/d/kgsl/proc/%d/mem", pid);
153    fp = fopen(tmp, "r");
154    if (fp == 0) {
155        //ALOGI("Unable to open: %s", tmp);
156        return 0;
157    }
158
159    while (true) {
160        if (fgets(line, 1024, fp) == NULL) {
161            break;
162        }
163
164        //ALOGI("Read: %s", line);
165
166        // Format is:
167        //  gpuaddr useraddr     size    id flags       type            usage sglen
168        // 54676000 54676000     4096     1 ----p     gpumem      arraybuffer     1
169        //
170        // If useraddr is 0, this is gpu mem not otherwise accounted
171        // against the process.
172
173        // Make sure line is long enough.
174        int i = 0;
175        while (i < 9) {
176            if (line[i] == 0) {
177                break;
178            }
179            i++;
180        }
181        if (i < 9) {
182            //ALOGI("Early line term!");
183            continue;
184        }
185
186        // Look to see if useraddr is 00000000.
187        while (i < 17) {
188            if (line[i] != '0') {
189                break;
190            }
191            i++;
192        }
193        if (i < 17) {
194            //ALOGI("useraddr not 0!");
195            continue;
196        }
197
198        uss += atoi(line + i);
199        //ALOGI("Uss now: %ld", uss);
200    }
201
202    fclose(fp);
203
204    // Convert from bytes to KB.
205    return uss / 1024;
206}
207
208static void read_mapinfo(FILE *fp, stats_t* stats)
209{
210    char line[1024];
211    int len, nameLen;
212    bool skip, done = false;
213
214    unsigned size = 0, resident = 0, pss = 0, swappable_pss = 0;
215    float sharing_proportion = 0.0;
216    unsigned shared_clean = 0, shared_dirty = 0;
217    unsigned private_clean = 0, private_dirty = 0;
218    bool is_swappable = false;
219    unsigned referenced = 0;
220    unsigned temp;
221
222    unsigned long int start;
223    unsigned long int end = 0;
224    unsigned long int prevEnd = 0;
225    char* name;
226    int name_pos;
227
228    int whichHeap = HEAP_UNKNOWN;
229    int subHeap = HEAP_UNKNOWN;
230    int prevHeap = HEAP_UNKNOWN;
231
232    if(fgets(line, sizeof(line), fp) == 0) return;
233
234    while (!done) {
235        prevHeap = whichHeap;
236        prevEnd = end;
237        whichHeap = HEAP_UNKNOWN;
238        subHeap = HEAP_UNKNOWN;
239        skip = false;
240        is_swappable = false;
241
242        len = strlen(line);
243        if (len < 1) return;
244        line[--len] = 0;
245
246        if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
247            skip = true;
248        } else {
249            while (isspace(line[name_pos])) {
250                name_pos += 1;
251            }
252            name = line + name_pos;
253            nameLen = strlen(name);
254
255            if ((strstr(name, "[heap]") == name)) {
256                whichHeap = HEAP_NATIVE;
257            } else if (strncmp(name, "/dev/ashmem", 11) == 0) {
258                if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) {
259                    whichHeap = HEAP_DALVIK_OTHER;
260                    if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) {
261                        subHeap = HEAP_DALVIK_LINEARALLOC;
262                    } else if ((strstr(name, "/dev/ashmem/dalvik-mark") == name) ||
263                               (strstr(name, "/dev/ashmem/dalvik-allocspace alloc space live-bitmap") == name) ||
264                               (strstr(name, "/dev/ashmem/dalvik-allocspace alloc space mark-bitmap") == name) ||
265                               (strstr(name, "/dev/ashmem/dalvik-card table") == name) ||
266                               (strstr(name, "/dev/ashmem/dalvik-allocation stack") == name) ||
267                               (strstr(name, "/dev/ashmem/dalvik-live stack") == name) ||
268                               (strstr(name, "/dev/ashmem/dalvik-imagespace") == name) ||
269                               (strstr(name, "/dev/ashmem/dalvik-bitmap") == name) ||
270                               (strstr(name, "/dev/ashmem/dalvik-card-table") == name) ||
271                               (strstr(name, "/dev/ashmem/dalvik-mark-stack") == name) ||
272                               (strstr(name, "/dev/ashmem/dalvik-aux-structure") == name)) {
273                        subHeap = HEAP_DALVIK_ACCOUNTING;
274                    } else if (strstr(name, "/dev/ashmem/dalvik-large") == name) {
275                        whichHeap = HEAP_DALVIK;
276                        subHeap = HEAP_DALVIK_LARGE;
277                    } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name) {
278                        subHeap = HEAP_DALVIK_CODE_CACHE;
279                    } else {
280                        // This is the regular Dalvik heap.
281                        whichHeap = HEAP_DALVIK;
282                        subHeap = HEAP_DALVIK_NORMAL;
283                    }
284                } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
285                    whichHeap = HEAP_CURSOR;
286                } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) {
287                    whichHeap = HEAP_NATIVE;
288                } else {
289                    whichHeap = HEAP_ASHMEM;
290                }
291            } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) {
292                whichHeap = HEAP_NATIVE;
293            } else if (strncmp(name, "[stack", 6) == 0) {
294                whichHeap = HEAP_STACK;
295            } else if (strncmp(name, "/dev/", 5) == 0) {
296                whichHeap = HEAP_UNKNOWN_DEV;
297            } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
298                whichHeap = HEAP_SO;
299                is_swappable = true;
300            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
301                whichHeap = HEAP_JAR;
302                is_swappable = true;
303            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
304                whichHeap = HEAP_APK;
305                is_swappable = true;
306            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
307                whichHeap = HEAP_TTF;
308                is_swappable = true;
309            } else if ((nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) ||
310                       (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
311                whichHeap = HEAP_DEX;
312                is_swappable = true;
313            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
314                whichHeap = HEAP_OAT;
315                is_swappable = true;
316            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
317                whichHeap = HEAP_ART;
318                is_swappable = true;
319            } else if (strncmp(name, "[anon:", 6) == 0) {
320                whichHeap = HEAP_UNKNOWN;
321            } else if (nameLen > 0) {
322                whichHeap = HEAP_UNKNOWN_MAP;
323            } else if (start == prevEnd && prevHeap == HEAP_SO) {
324                // bss section of a shared library.
325                whichHeap = HEAP_SO;
326            }
327        }
328
329        //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
330        //    isSqliteHeap, line);
331
332        while (true) {
333            if (fgets(line, 1024, fp) == 0) {
334                done = true;
335                break;
336            }
337
338            if (sscanf(line, "Size: %d kB", &temp) == 1) {
339                size = temp;
340            } else if (sscanf(line, "Rss: %d kB", &temp) == 1) {
341                resident = temp;
342            } else if (sscanf(line, "Pss: %d kB", &temp) == 1) {
343                pss = temp;
344            } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
345                shared_clean = temp;
346            } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
347                shared_dirty = temp;
348            } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
349                private_clean = temp;
350            } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
351                private_dirty = temp;
352            } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) {
353                referenced = temp;
354            } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
355                // looks like a new mapping
356                // example: "10000000-10001000 ---p 10000000 00:00 0"
357                break;
358            }
359        }
360
361        if (!skip) {
362            if (is_swappable && (pss > 0)) {
363                sharing_proportion = 0.0;
364                if ((shared_clean > 0) || (shared_dirty > 0)) {
365                    sharing_proportion = (pss - private_clean - private_dirty)/(shared_clean+shared_dirty);
366                }
367                swappable_pss = (sharing_proportion*shared_clean) + private_clean;
368            } else
369                swappable_pss = 0;
370
371            stats[whichHeap].pss += pss;
372            stats[whichHeap].swappablePss += swappable_pss;
373            stats[whichHeap].privateDirty += private_dirty;
374            stats[whichHeap].sharedDirty += shared_dirty;
375            stats[whichHeap].privateClean += private_clean;
376            stats[whichHeap].sharedClean += shared_clean;
377            if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
378                stats[subHeap].pss += pss;
379                stats[subHeap].swappablePss += swappable_pss;
380                stats[subHeap].privateDirty += private_dirty;
381                stats[subHeap].sharedDirty += shared_dirty;
382                stats[subHeap].privateClean += private_clean;
383                stats[subHeap].sharedClean += shared_clean;
384            }
385        }
386    }
387}
388
389static void load_maps(int pid, stats_t* stats)
390{
391    char tmp[128];
392    FILE *fp;
393
394    sprintf(tmp, "/proc/%d/smaps", pid);
395    fp = fopen(tmp, "r");
396    if (fp == 0) return;
397
398    read_mapinfo(fp, stats);
399    fclose(fp);
400}
401
402static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
403        jint pid, jobject object)
404{
405    stats_t stats[_NUM_HEAP];
406    memset(&stats, 0, sizeof(stats));
407
408
409    load_maps(pid, stats);
410
411    jlong gpu = read_gpu_mem(pid);
412    stats[HEAP_GPU].pss += gpu;
413    stats[HEAP_GPU].privateDirty += gpu;
414
415    for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
416        stats[HEAP_UNKNOWN].pss += stats[i].pss;
417        stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
418        stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
419        stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
420        stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
421        stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
422    }
423
424    for (int i=0; i<_NUM_CORE_HEAP; i++) {
425        env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
426        env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
427        env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
428        env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
429        env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
430        env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
431    }
432
433
434    jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
435
436    jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
437    if (otherArray == NULL) {
438        return;
439    }
440
441    int j=0;
442    for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
443        otherArray[j++] = stats[i].pss;
444        otherArray[j++] = stats[i].swappablePss;
445        otherArray[j++] = stats[i].privateDirty;
446        otherArray[j++] = stats[i].sharedDirty;
447        otherArray[j++] = stats[i].privateClean;
448        otherArray[j++] = stats[i].sharedClean;
449    }
450
451    env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
452}
453
454static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
455{
456    android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
457}
458
459static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss)
460{
461    char line[1024];
462    jlong pss = 0;
463    jlong uss = 0;
464    unsigned temp;
465
466    char tmp[128];
467    FILE *fp;
468
469    pss = uss = read_gpu_mem(pid);
470
471    sprintf(tmp, "/proc/%d/smaps", pid);
472    fp = fopen(tmp, "r");
473
474    if (fp != 0) {
475        while (true) {
476            if (fgets(line, 1024, fp) == NULL) {
477                break;
478            }
479
480            if (line[0] == 'P') {
481                if (strncmp(line, "Pss:", 4) == 0) {
482                    char* c = line + 4;
483                    while (*c != 0 && (*c < '0' || *c > '9')) {
484                        c++;
485                    }
486                    pss += atoi(c);
487                } else if (strncmp(line, "Private_Clean:", 14)
488                        || strncmp(line, "Private_Dirty:", 14)) {
489                    char* c = line + 14;
490                    while (*c != 0 && (*c < '0' || *c > '9')) {
491                        c++;
492                    }
493                    uss += atoi(c);
494                }
495            }
496        }
497
498        fclose(fp);
499    }
500
501    if (outUss != NULL) {
502        if (env->GetArrayLength(outUss) >= 1) {
503            jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
504            if (outUssArray != NULL) {
505                outUssArray[0] = uss;
506            }
507            env->ReleaseLongArrayElements(outUss, outUssArray, 0);
508        }
509    }
510
511    return pss;
512}
513
514static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
515{
516    return android_os_Debug_getPssPid(env, clazz, getpid(), NULL);
517}
518
519static jint read_binder_stat(const char* stat)
520{
521    FILE* fp = fopen(BINDER_STATS, "r");
522    if (fp == NULL) {
523        return -1;
524    }
525
526    char line[1024];
527
528    char compare[128];
529    int len = snprintf(compare, 128, "proc %d", getpid());
530
531    // loop until we have the block that represents this process
532    do {
533        if (fgets(line, 1024, fp) == 0) {
534            return -1;
535        }
536    } while (strncmp(compare, line, len));
537
538    // now that we have this process, read until we find the stat that we are looking for
539    len = snprintf(compare, 128, "  %s: ", stat);
540
541    do {
542        if (fgets(line, 1024, fp) == 0) {
543            return -1;
544        }
545    } while (strncmp(compare, line, len));
546
547    // we have the line, now increment the line ptr to the value
548    char* ptr = line + len;
549    return atoi(ptr);
550}
551
552static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
553{
554    return read_binder_stat("bcTRANSACTION");
555}
556
557static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
558{
559    return read_binder_stat("brTRANSACTION");
560}
561
562// these are implemented in android_util_Binder.cpp
563jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
564jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
565jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
566
567
568/* pulled out of bionic */
569extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
570    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
571extern "C" void free_malloc_leak_info(uint8_t* info);
572#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
573#define BACKTRACE_SIZE          32
574
575/*
576 * This is a qsort() callback.
577 *
578 * See dumpNativeHeap() for comments about the data format and sort order.
579 */
580static int compareHeapRecords(const void* vrec1, const void* vrec2)
581{
582    const size_t* rec1 = (const size_t*) vrec1;
583    const size_t* rec2 = (const size_t*) vrec2;
584    size_t size1 = *rec1;
585    size_t size2 = *rec2;
586
587    if (size1 < size2) {
588        return 1;
589    } else if (size1 > size2) {
590        return -1;
591    }
592
593    intptr_t* bt1 = (intptr_t*)(rec1 + 2);
594    intptr_t* bt2 = (intptr_t*)(rec2 + 2);
595    for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
596        intptr_t addr1 = bt1[idx];
597        intptr_t addr2 = bt2[idx];
598        if (addr1 == addr2) {
599            if (addr1 == 0)
600                break;
601            continue;
602        }
603        if (addr1 < addr2) {
604            return -1;
605        } else if (addr1 > addr2) {
606            return 1;
607        }
608    }
609
610    return 0;
611}
612
613/*
614 * The get_malloc_leak_info() call returns an array of structs that
615 * look like this:
616 *
617 *   size_t size
618 *   size_t allocations
619 *   intptr_t backtrace[32]
620 *
621 * "size" is the size of the allocation, "backtrace" is a fixed-size
622 * array of function pointers, and "allocations" is the number of
623 * allocations with the exact same size and backtrace.
624 *
625 * The entries are sorted by descending total size (i.e. size*allocations)
626 * then allocation count.  For best results with "diff" we'd like to sort
627 * primarily by individual size then stack trace.  Since the entries are
628 * fixed-size, and we're allowed (by the current implementation) to mangle
629 * them, we can do this in place.
630 */
631static void dumpNativeHeap(FILE* fp)
632{
633    uint8_t* info = NULL;
634    size_t overallSize, infoSize, totalMemory, backtraceSize;
635
636    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
637        &backtraceSize);
638    if (info == NULL) {
639        fprintf(fp, "Native heap dump not available. To enable, run these"
640                    " commands (requires root):\n");
641        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
642        fprintf(fp, "$ adb shell stop\n");
643        fprintf(fp, "$ adb shell start\n");
644        return;
645    }
646    assert(infoSize != 0);
647    assert(overallSize % infoSize == 0);
648
649    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
650
651    size_t recordCount = overallSize / infoSize;
652    fprintf(fp, "Total memory: %zu\n", totalMemory);
653    fprintf(fp, "Allocation records: %zd\n", recordCount);
654    if (backtraceSize != BACKTRACE_SIZE) {
655        fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
656            backtraceSize, BACKTRACE_SIZE);
657    }
658    fprintf(fp, "\n");
659
660    /* re-sort the entries */
661    qsort(info, recordCount, infoSize, compareHeapRecords);
662
663    /* dump the entries to the file */
664    const uint8_t* ptr = info;
665    for (size_t idx = 0; idx < recordCount; idx++) {
666        size_t size = *(size_t*) ptr;
667        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
668        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
669
670        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
671                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
672                size & ~SIZE_FLAG_ZYGOTE_CHILD,
673                allocations);
674        for (size_t bt = 0; bt < backtraceSize; bt++) {
675            if (backtrace[bt] == 0) {
676                break;
677            } else {
678                fprintf(fp, " %08x", backtrace[bt]);
679            }
680        }
681        fprintf(fp, "\n");
682
683        ptr += infoSize;
684    }
685
686    free_malloc_leak_info(info);
687
688    fprintf(fp, "MAPS\n");
689    const char* maps = "/proc/self/maps";
690    FILE* in = fopen(maps, "r");
691    if (in == NULL) {
692        fprintf(fp, "Could not open %s\n", maps);
693        return;
694    }
695    char buf[BUFSIZ];
696    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
697        fwrite(buf, sizeof(char), n, fp);
698    }
699    fclose(in);
700
701    fprintf(fp, "END\n");
702}
703
704/*
705 * Dump the native heap, writing human-readable output to the specified
706 * file descriptor.
707 */
708static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
709    jobject fileDescriptor)
710{
711    if (fileDescriptor == NULL) {
712        jniThrowNullPointerException(env, "fd == null");
713        return;
714    }
715    int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
716    if (origFd < 0) {
717        jniThrowRuntimeException(env, "Invalid file descriptor");
718        return;
719    }
720
721    /* dup() the descriptor so we don't close the original with fclose() */
722    int fd = dup(origFd);
723    if (fd < 0) {
724        ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
725        jniThrowRuntimeException(env, "dup() failed");
726        return;
727    }
728
729    FILE* fp = fdopen(fd, "w");
730    if (fp == NULL) {
731        ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
732        close(fd);
733        jniThrowRuntimeException(env, "fdopen() failed");
734        return;
735    }
736
737    ALOGD("Native heap dump starting...\n");
738    dumpNativeHeap(fp);
739    ALOGD("Native heap dump complete.\n");
740
741    fclose(fp);
742}
743
744
745static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz,
746    jint pid, jstring fileName)
747{
748    if (fileName == NULL) {
749        jniThrowNullPointerException(env, "file == null");
750        return;
751    }
752    const jchar* str = env->GetStringCritical(fileName, 0);
753    String8 fileName8;
754    if (str) {
755        fileName8 = String8(str, env->GetStringLength(fileName));
756        env->ReleaseStringCritical(fileName, str);
757    }
758
759    int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
760    if (fd < 0) {
761        fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
762        return;
763    }
764
765    if (lseek(fd, 0, SEEK_END) < 0) {
766        fprintf(stderr, "lseek: %s\n", strerror(errno));
767    } else {
768        dump_backtrace_to_file(pid, fd);
769    }
770
771    close(fd);
772}
773
774/*
775 * JNI registration.
776 */
777
778static JNINativeMethod gMethods[] = {
779    { "getNativeHeapSize",      "()J",
780            (void*) android_os_Debug_getNativeHeapSize },
781    { "getNativeHeapAllocatedSize", "()J",
782            (void*) android_os_Debug_getNativeHeapAllocatedSize },
783    { "getNativeHeapFreeSize",  "()J",
784            (void*) android_os_Debug_getNativeHeapFreeSize },
785    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
786            (void*) android_os_Debug_getDirtyPages },
787    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
788            (void*) android_os_Debug_getDirtyPagesPid },
789    { "getPss",                 "()J",
790            (void*) android_os_Debug_getPss },
791    { "getPss",                 "(I[J)J",
792            (void*) android_os_Debug_getPssPid },
793    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
794            (void*) android_os_Debug_dumpNativeHeap },
795    { "getBinderSentTransactions", "()I",
796            (void*) android_os_Debug_getBinderSentTransactions },
797    { "getBinderReceivedTransactions", "()I",
798            (void*) android_os_getBinderReceivedTransactions },
799    { "getBinderLocalObjectCount", "()I",
800            (void*)android_os_Debug_getLocalObjectCount },
801    { "getBinderProxyObjectCount", "()I",
802            (void*)android_os_Debug_getProxyObjectCount },
803    { "getBinderDeathObjectCount", "()I",
804            (void*)android_os_Debug_getDeathObjectCount },
805    { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V",
806            (void*)android_os_Debug_dumpNativeBacktraceToFile },
807};
808
809int register_android_os_Debug(JNIEnv *env)
810{
811    jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
812
813    // Sanity check the number of other statistics expected in Java matches here.
814    jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
815    jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
816    jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
817    jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
818    int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
819    if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
820        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
821                             "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
822                             numOtherStats+numDvkStats, expectedNumOtherStats);
823        return JNI_ERR;
824    }
825
826    otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
827
828    for (int i=0; i<_NUM_CORE_HEAP; i++) {
829        stat_fields[i].pss_field =
830                env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
831        stat_fields[i].pssSwappable_field =
832                env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
833        stat_fields[i].privateDirty_field =
834                env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
835        stat_fields[i].sharedDirty_field =
836                env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
837        stat_fields[i].privateClean_field =
838                env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
839        stat_fields[i].sharedClean_field =
840                env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
841    }
842
843    return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
844}
845
846}; // namespace android
847