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