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