android_os_Debug.cpp revision 8e69257a9c7e9c1781e1f53d8856358ada38921d
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 void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
520{
521    char buffer[1024];
522    int numFound = 0;
523
524    if (out == NULL) {
525        jniThrowNullPointerException(env, "out == null");
526        return;
527    }
528
529    int fd = open("/proc/meminfo", O_RDONLY);
530
531    if (fd < 0) {
532        printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
533        return;
534    }
535
536    const int len = read(fd, buffer, sizeof(buffer)-1);
537    close(fd);
538
539    if (len < 0) {
540        printf("Empty /proc/meminfo");
541        return;
542    }
543    buffer[len] = 0;
544
545    static const char* const tags[] = {
546            "MemTotal:",
547            "MemFree:",
548            "Buffers:",
549            "Cached:",
550            "Shmem:",
551            "Slab:",
552            NULL
553    };
554    static const int tagsLen[] = {
555            9,
556            8,
557            8,
558            7,
559            6,
560            5,
561            0
562    };
563    long mem[] = { 0, 0, 0, 0, 0, 0 };
564
565    char* p = buffer;
566    while (*p && numFound < 6) {
567        int i = 0;
568        while (tags[i]) {
569            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
570                p += tagsLen[i];
571                while (*p == ' ') p++;
572                char* num = p;
573                while (*p >= '0' && *p <= '9') p++;
574                if (*p != 0) {
575                    *p = 0;
576                    p++;
577                }
578                mem[i] = atoll(num);
579                numFound++;
580                break;
581            }
582            i++;
583        }
584        while (*p && *p != '\n') {
585            p++;
586        }
587        if (*p) p++;
588    }
589
590    int maxNum = env->GetArrayLength(out);
591    jlong* outArray = env->GetLongArrayElements(out, 0);
592    if (outArray != NULL) {
593        for (int i=0; i<maxNum && tags[i]; i++) {
594            outArray[i] = mem[i];
595        }
596    }
597    env->ReleaseLongArrayElements(out, outArray, 0);
598}
599
600static jint read_binder_stat(const char* stat)
601{
602    FILE* fp = fopen(BINDER_STATS, "r");
603    if (fp == NULL) {
604        return -1;
605    }
606
607    char line[1024];
608
609    char compare[128];
610    int len = snprintf(compare, 128, "proc %d", getpid());
611
612    // loop until we have the block that represents this process
613    do {
614        if (fgets(line, 1024, fp) == 0) {
615            return -1;
616        }
617    } while (strncmp(compare, line, len));
618
619    // now that we have this process, read until we find the stat that we are looking for
620    len = snprintf(compare, 128, "  %s: ", stat);
621
622    do {
623        if (fgets(line, 1024, fp) == 0) {
624            return -1;
625        }
626    } while (strncmp(compare, line, len));
627
628    // we have the line, now increment the line ptr to the value
629    char* ptr = line + len;
630    return atoi(ptr);
631}
632
633static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
634{
635    return read_binder_stat("bcTRANSACTION");
636}
637
638static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
639{
640    return read_binder_stat("brTRANSACTION");
641}
642
643// these are implemented in android_util_Binder.cpp
644jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
645jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
646jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
647
648
649/* pulled out of bionic */
650extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
651    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
652extern "C" void free_malloc_leak_info(uint8_t* info);
653#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
654#define BACKTRACE_SIZE          32
655
656/*
657 * This is a qsort() callback.
658 *
659 * See dumpNativeHeap() for comments about the data format and sort order.
660 */
661static int compareHeapRecords(const void* vrec1, const void* vrec2)
662{
663    const size_t* rec1 = (const size_t*) vrec1;
664    const size_t* rec2 = (const size_t*) vrec2;
665    size_t size1 = *rec1;
666    size_t size2 = *rec2;
667
668    if (size1 < size2) {
669        return 1;
670    } else if (size1 > size2) {
671        return -1;
672    }
673
674    intptr_t* bt1 = (intptr_t*)(rec1 + 2);
675    intptr_t* bt2 = (intptr_t*)(rec2 + 2);
676    for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
677        intptr_t addr1 = bt1[idx];
678        intptr_t addr2 = bt2[idx];
679        if (addr1 == addr2) {
680            if (addr1 == 0)
681                break;
682            continue;
683        }
684        if (addr1 < addr2) {
685            return -1;
686        } else if (addr1 > addr2) {
687            return 1;
688        }
689    }
690
691    return 0;
692}
693
694/*
695 * The get_malloc_leak_info() call returns an array of structs that
696 * look like this:
697 *
698 *   size_t size
699 *   size_t allocations
700 *   intptr_t backtrace[32]
701 *
702 * "size" is the size of the allocation, "backtrace" is a fixed-size
703 * array of function pointers, and "allocations" is the number of
704 * allocations with the exact same size and backtrace.
705 *
706 * The entries are sorted by descending total size (i.e. size*allocations)
707 * then allocation count.  For best results with "diff" we'd like to sort
708 * primarily by individual size then stack trace.  Since the entries are
709 * fixed-size, and we're allowed (by the current implementation) to mangle
710 * them, we can do this in place.
711 */
712static void dumpNativeHeap(FILE* fp)
713{
714    uint8_t* info = NULL;
715    size_t overallSize, infoSize, totalMemory, backtraceSize;
716
717    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
718        &backtraceSize);
719    if (info == NULL) {
720        fprintf(fp, "Native heap dump not available. To enable, run these"
721                    " commands (requires root):\n");
722        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
723        fprintf(fp, "$ adb shell stop\n");
724        fprintf(fp, "$ adb shell start\n");
725        return;
726    }
727    assert(infoSize != 0);
728    assert(overallSize % infoSize == 0);
729
730    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
731
732    size_t recordCount = overallSize / infoSize;
733    fprintf(fp, "Total memory: %zu\n", totalMemory);
734    fprintf(fp, "Allocation records: %zd\n", recordCount);
735    if (backtraceSize != BACKTRACE_SIZE) {
736        fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
737            backtraceSize, BACKTRACE_SIZE);
738    }
739    fprintf(fp, "\n");
740
741    /* re-sort the entries */
742    qsort(info, recordCount, infoSize, compareHeapRecords);
743
744    /* dump the entries to the file */
745    const uint8_t* ptr = info;
746    for (size_t idx = 0; idx < recordCount; idx++) {
747        size_t size = *(size_t*) ptr;
748        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
749        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
750
751        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
752                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
753                size & ~SIZE_FLAG_ZYGOTE_CHILD,
754                allocations);
755        for (size_t bt = 0; bt < backtraceSize; bt++) {
756            if (backtrace[bt] == 0) {
757                break;
758            } else {
759                fprintf(fp, " %08x", backtrace[bt]);
760            }
761        }
762        fprintf(fp, "\n");
763
764        ptr += infoSize;
765    }
766
767    free_malloc_leak_info(info);
768
769    fprintf(fp, "MAPS\n");
770    const char* maps = "/proc/self/maps";
771    FILE* in = fopen(maps, "r");
772    if (in == NULL) {
773        fprintf(fp, "Could not open %s\n", maps);
774        return;
775    }
776    char buf[BUFSIZ];
777    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
778        fwrite(buf, sizeof(char), n, fp);
779    }
780    fclose(in);
781
782    fprintf(fp, "END\n");
783}
784
785/*
786 * Dump the native heap, writing human-readable output to the specified
787 * file descriptor.
788 */
789static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
790    jobject fileDescriptor)
791{
792    if (fileDescriptor == NULL) {
793        jniThrowNullPointerException(env, "fd == null");
794        return;
795    }
796    int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
797    if (origFd < 0) {
798        jniThrowRuntimeException(env, "Invalid file descriptor");
799        return;
800    }
801
802    /* dup() the descriptor so we don't close the original with fclose() */
803    int fd = dup(origFd);
804    if (fd < 0) {
805        ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
806        jniThrowRuntimeException(env, "dup() failed");
807        return;
808    }
809
810    FILE* fp = fdopen(fd, "w");
811    if (fp == NULL) {
812        ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
813        close(fd);
814        jniThrowRuntimeException(env, "fdopen() failed");
815        return;
816    }
817
818    ALOGD("Native heap dump starting...\n");
819    dumpNativeHeap(fp);
820    ALOGD("Native heap dump complete.\n");
821
822    fclose(fp);
823}
824
825
826static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz,
827    jint pid, jstring fileName)
828{
829    if (fileName == NULL) {
830        jniThrowNullPointerException(env, "file == null");
831        return;
832    }
833    const jchar* str = env->GetStringCritical(fileName, 0);
834    String8 fileName8;
835    if (str) {
836        fileName8 = String8(str, env->GetStringLength(fileName));
837        env->ReleaseStringCritical(fileName, str);
838    }
839
840    int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
841    if (fd < 0) {
842        fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
843        return;
844    }
845
846    if (lseek(fd, 0, SEEK_END) < 0) {
847        fprintf(stderr, "lseek: %s\n", strerror(errno));
848    } else {
849        dump_backtrace_to_file(pid, fd);
850    }
851
852    close(fd);
853}
854
855/*
856 * JNI registration.
857 */
858
859static JNINativeMethod gMethods[] = {
860    { "getNativeHeapSize",      "()J",
861            (void*) android_os_Debug_getNativeHeapSize },
862    { "getNativeHeapAllocatedSize", "()J",
863            (void*) android_os_Debug_getNativeHeapAllocatedSize },
864    { "getNativeHeapFreeSize",  "()J",
865            (void*) android_os_Debug_getNativeHeapFreeSize },
866    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
867            (void*) android_os_Debug_getDirtyPages },
868    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
869            (void*) android_os_Debug_getDirtyPagesPid },
870    { "getPss",                 "()J",
871            (void*) android_os_Debug_getPss },
872    { "getPss",                 "(I[J)J",
873            (void*) android_os_Debug_getPssPid },
874    { "getMemInfo",             "([J)V",
875            (void*) android_os_Debug_getMemInfo },
876    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
877            (void*) android_os_Debug_dumpNativeHeap },
878    { "getBinderSentTransactions", "()I",
879            (void*) android_os_Debug_getBinderSentTransactions },
880    { "getBinderReceivedTransactions", "()I",
881            (void*) android_os_getBinderReceivedTransactions },
882    { "getBinderLocalObjectCount", "()I",
883            (void*)android_os_Debug_getLocalObjectCount },
884    { "getBinderProxyObjectCount", "()I",
885            (void*)android_os_Debug_getProxyObjectCount },
886    { "getBinderDeathObjectCount", "()I",
887            (void*)android_os_Debug_getDeathObjectCount },
888    { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V",
889            (void*)android_os_Debug_dumpNativeBacktraceToFile },
890};
891
892int register_android_os_Debug(JNIEnv *env)
893{
894    jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
895
896    // Sanity check the number of other statistics expected in Java matches here.
897    jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
898    jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
899    jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
900    jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
901    int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
902    if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
903        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
904                             "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
905                             numOtherStats+numDvkStats, expectedNumOtherStats);
906        return JNI_ERR;
907    }
908
909    otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
910
911    for (int i=0; i<_NUM_CORE_HEAP; i++) {
912        stat_fields[i].pss_field =
913                env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
914        stat_fields[i].pssSwappable_field =
915                env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
916        stat_fields[i].privateDirty_field =
917                env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
918        stat_fields[i].sharedDirty_field =
919                env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
920        stat_fields[i].privateClean_field =
921                env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
922        stat_fields[i].sharedClean_field =
923                env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
924    }
925
926    return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
927}
928
929}; // namespace android
930