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