android_os_Debug.cpp revision b57dd722f1dc0663417da37d3a82f8283ad3c982
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                            whichHeap = HEAP_DALVIK;
299                            subHeap = HEAP_DALVIK_LARGE;
300                        } else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) {
301                            whichHeap = HEAP_DALVIK;
302                            subHeap = HEAP_DALVIK_NON_MOVING;
303                        } else if (strstr(name, "/dev/ashmem/dalvik-zygote space") == name) {
304                            whichHeap = HEAP_DALVIK;
305                            subHeap = HEAP_DALVIK_ZYGOTE;
306                        } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) {
307                            subHeap = HEAP_DALVIK_INDIRECT_REFERENCE_TABLE;
308                        } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name) {
309                            subHeap = HEAP_DALVIK_CODE_CACHE;
310                        } else {
311                            subHeap = HEAP_DALVIK_ACCOUNTING;  // Default to accounting.
312                        }
313                    } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
314                        whichHeap = HEAP_CURSOR;
315                    } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) {
316                        whichHeap = HEAP_NATIVE;
317                    } else {
318                        whichHeap = HEAP_ASHMEM;
319                    }
320                } else {
321                    whichHeap = HEAP_UNKNOWN_DEV;
322                }
323            } else if (strncmp(name, "[anon:", 6) == 0) {
324                whichHeap = HEAP_UNKNOWN;
325            } else if (nameLen > 0) {
326                whichHeap = HEAP_UNKNOWN_MAP;
327            } else if (start == prevEnd && prevHeap == HEAP_SO) {
328                // bss section of a shared library.
329                whichHeap = HEAP_SO;
330            }
331        }
332
333        //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
334        //    isSqliteHeap, line);
335
336        shared_clean = 0;
337        shared_dirty = 0;
338        private_clean = 0;
339        private_dirty = 0;
340        swapped_out = 0;
341
342        while (true) {
343            if (fgets(line, 1024, fp) == 0) {
344                done = true;
345                break;
346            }
347
348            if (line[0] == 'S' && sscanf(line, "Size: %d kB", &temp) == 1) {
349                /* size = temp; */
350            } else if (line[0] == 'R' && sscanf(line, "Rss: %d kB", &temp) == 1) {
351                /* resident = temp; */
352            } else if (line[0] == 'P' && sscanf(line, "Pss: %d kB", &temp) == 1) {
353                pss = temp;
354            } else if (line[0] == 'S' && sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
355                shared_clean = temp;
356            } else if (line[0] == 'S' && sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
357                shared_dirty = temp;
358            } else if (line[0] == 'P' && sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
359                private_clean = temp;
360            } else if (line[0] == 'P' && sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
361                private_dirty = temp;
362            } else if (line[0] == 'R' && sscanf(line, "Referenced: %d kB", &temp) == 1) {
363                /* referenced = temp; */
364            } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
365                swapped_out = temp;
366            } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
367                // looks like a new mapping
368                // example: "10000000-10001000 ---p 10000000 00:00 0"
369                break;
370            }
371        }
372
373        if (!skip) {
374            if (is_swappable && (pss > 0)) {
375                sharing_proportion = 0.0;
376                if ((shared_clean > 0) || (shared_dirty > 0)) {
377                    sharing_proportion = (pss - private_clean
378                            - private_dirty)/(shared_clean+shared_dirty);
379                }
380                swappable_pss = (sharing_proportion*shared_clean) + private_clean;
381            } else
382                swappable_pss = 0;
383
384            stats[whichHeap].pss += pss;
385            stats[whichHeap].swappablePss += swappable_pss;
386            stats[whichHeap].privateDirty += private_dirty;
387            stats[whichHeap].sharedDirty += shared_dirty;
388            stats[whichHeap].privateClean += private_clean;
389            stats[whichHeap].sharedClean += shared_clean;
390            stats[whichHeap].swappedOut += swapped_out;
391            if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
392                stats[subHeap].pss += pss;
393                stats[subHeap].swappablePss += swappable_pss;
394                stats[subHeap].privateDirty += private_dirty;
395                stats[subHeap].sharedDirty += shared_dirty;
396                stats[subHeap].privateClean += private_clean;
397                stats[subHeap].sharedClean += shared_clean;
398                stats[subHeap].swappedOut += swapped_out;
399            }
400        }
401    }
402}
403
404static void load_maps(int pid, stats_t* stats)
405{
406    char tmp[128];
407    FILE *fp;
408
409    sprintf(tmp, "/proc/%d/smaps", pid);
410    fp = fopen(tmp, "r");
411    if (fp == 0) return;
412
413    read_mapinfo(fp, stats);
414    fclose(fp);
415}
416
417static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
418        jint pid, jobject object)
419{
420    stats_t stats[_NUM_HEAP];
421    memset(&stats, 0, sizeof(stats));
422
423    load_maps(pid, stats);
424
425    struct graphics_memory_pss graphics_mem;
426    if (read_memtrack_memory(pid, &graphics_mem) == 0) {
427        stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
428        stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
429        stats[HEAP_GL].pss = graphics_mem.gl;
430        stats[HEAP_GL].privateDirty = graphics_mem.gl;
431        stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
432        stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
433    }
434
435    for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
436        stats[HEAP_UNKNOWN].pss += stats[i].pss;
437        stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
438        stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
439        stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
440        stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
441        stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
442        stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
443    }
444
445    for (int i=0; i<_NUM_CORE_HEAP; i++) {
446        env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
447        env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
448        env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
449        env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
450        env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
451        env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
452        env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
453    }
454
455
456    jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
457
458    jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
459    if (otherArray == NULL) {
460        return;
461    }
462
463    int j=0;
464    for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
465        otherArray[j++] = stats[i].pss;
466        otherArray[j++] = stats[i].swappablePss;
467        otherArray[j++] = stats[i].privateDirty;
468        otherArray[j++] = stats[i].sharedDirty;
469        otherArray[j++] = stats[i].privateClean;
470        otherArray[j++] = stats[i].sharedClean;
471        otherArray[j++] = stats[i].swappedOut;
472    }
473
474    env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
475}
476
477static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
478{
479    android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
480}
481
482static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss,
483        jlongArray outMemtrack)
484{
485    char line[1024];
486    jlong pss = 0;
487    jlong uss = 0;
488    jlong memtrack = 0;
489
490    char tmp[128];
491    FILE *fp;
492
493    struct graphics_memory_pss graphics_mem;
494    if (read_memtrack_memory(pid, &graphics_mem) == 0) {
495        pss = uss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
496    }
497
498    sprintf(tmp, "/proc/%d/smaps", pid);
499    fp = fopen(tmp, "r");
500
501    if (fp != 0) {
502        while (true) {
503            if (fgets(line, 1024, fp) == NULL) {
504                break;
505            }
506
507            if (line[0] == 'P') {
508                if (strncmp(line, "Pss:", 4) == 0) {
509                    char* c = line + 4;
510                    while (*c != 0 && (*c < '0' || *c > '9')) {
511                        c++;
512                    }
513                    pss += atoi(c);
514                } else if (strncmp(line, "Private_Clean:", 14) == 0
515                        || strncmp(line, "Private_Dirty:", 14) == 0) {
516                    char* c = line + 14;
517                    while (*c != 0 && (*c < '0' || *c > '9')) {
518                        c++;
519                    }
520                    uss += atoi(c);
521                }
522            }
523        }
524
525        fclose(fp);
526    }
527
528    if (outUss != NULL) {
529        if (env->GetArrayLength(outUss) >= 1) {
530            jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
531            if (outUssArray != NULL) {
532                outUssArray[0] = uss;
533            }
534            env->ReleaseLongArrayElements(outUss, outUssArray, 0);
535        }
536    }
537
538    if (outMemtrack != NULL) {
539        if (env->GetArrayLength(outMemtrack) >= 1) {
540            jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
541            if (outMemtrackArray != NULL) {
542                outMemtrackArray[0] = memtrack;
543            }
544            env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
545        }
546    }
547
548    return pss;
549}
550
551static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
552{
553    return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
554}
555
556static long get_allocated_vmalloc_memory() {
557    char line[1024];
558    long size, vmalloc_allocated_size = 0;
559    FILE* fp = fopen("/proc/vmallocinfo", "r");
560    if (fp == NULL) {
561        return 0;
562    }
563    while (true) {
564        if (fgets(line, 1024, fp) == NULL) {
565            break;
566        }
567
568        if (!strstr(line, "ioremap") && !strstr(line, "map_lowmem")) {
569            // Ignore ioremap and map_lowmem regions, since they don't actually consume memory
570            if (sscanf(line, "%*x-%*x %ld", &size) == 1) {
571                vmalloc_allocated_size += size;
572            }
573        }
574    }
575    fclose(fp);
576    return vmalloc_allocated_size;
577}
578
579enum {
580    MEMINFO_TOTAL,
581    MEMINFO_FREE,
582    MEMINFO_BUFFERS,
583    MEMINFO_CACHED,
584    MEMINFO_SHMEM,
585    MEMINFO_SLAB,
586    MEMINFO_SWAP_TOTAL,
587    MEMINFO_SWAP_FREE,
588    MEMINFO_ZRAM_TOTAL,
589    MEMINFO_MAPPED,
590    MEMINFO_VMALLOC_USED,
591    MEMINFO_PAGE_TABLES,
592    MEMINFO_KERNEL_STACK,
593    MEMINFO_COUNT
594};
595
596static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
597{
598    char buffer[1024];
599    size_t numFound = 0;
600
601    if (out == NULL) {
602        jniThrowNullPointerException(env, "out == null");
603        return;
604    }
605
606    int fd = open("/proc/meminfo", O_RDONLY);
607
608    if (fd < 0) {
609        ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
610        return;
611    }
612
613    int len = read(fd, buffer, sizeof(buffer)-1);
614    close(fd);
615
616    if (len < 0) {
617        ALOGW("Empty /proc/meminfo");
618        return;
619    }
620    buffer[len] = 0;
621
622    static const char* const tags[] = {
623            "MemTotal:",
624            "MemFree:",
625            "Buffers:",
626            "Cached:",
627            "Shmem:",
628            "Slab:",
629            "SwapTotal:",
630            "SwapFree:",
631            "ZRam:",
632            "Mapped:",
633            "VmallocUsed:",
634            "PageTables:",
635            "KernelStack:",
636            NULL
637    };
638    static const int tagsLen[] = {
639            9,
640            8,
641            8,
642            7,
643            6,
644            5,
645            10,
646            9,
647            5,
648            7,
649            12,
650            11,
651            12,
652            0
653    };
654    long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
655
656    char* p = buffer;
657    while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
658        int i = 0;
659        while (tags[i]) {
660            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
661                p += tagsLen[i];
662                while (*p == ' ') p++;
663                char* num = p;
664                while (*p >= '0' && *p <= '9') p++;
665                if (*p != 0) {
666                    *p = 0;
667                    p++;
668                }
669                mem[i] = atoll(num);
670                numFound++;
671                break;
672            }
673            i++;
674        }
675        while (*p && *p != '\n') {
676            p++;
677        }
678        if (*p) p++;
679    }
680
681    fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
682    if (fd >= 0) {
683        len = read(fd, buffer, sizeof(buffer)-1);
684        close(fd);
685        if (len > 0) {
686            buffer[len] = 0;
687            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
688        }
689    }
690    // Recompute Vmalloc Used since the value in meminfo
691    // doesn't account for I/O remapping which doesn't use RAM.
692    mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
693
694    int maxNum = env->GetArrayLength(out);
695    if (maxNum > MEMINFO_COUNT) {
696        maxNum = MEMINFO_COUNT;
697    }
698    jlong* outArray = env->GetLongArrayElements(out, 0);
699    if (outArray != NULL) {
700        for (int i=0; i<maxNum; i++) {
701            outArray[i] = mem[i];
702        }
703    }
704    env->ReleaseLongArrayElements(out, outArray, 0);
705}
706
707
708static jint read_binder_stat(const char* stat)
709{
710    FILE* fp = fopen(BINDER_STATS, "r");
711    if (fp == NULL) {
712        return -1;
713    }
714
715    char line[1024];
716
717    char compare[128];
718    int len = snprintf(compare, 128, "proc %d", getpid());
719
720    // loop until we have the block that represents this process
721    do {
722        if (fgets(line, 1024, fp) == 0) {
723            fclose(fp);
724            return -1;
725        }
726    } while (strncmp(compare, line, len));
727
728    // now that we have this process, read until we find the stat that we are looking for
729    len = snprintf(compare, 128, "  %s: ", stat);
730
731    do {
732        if (fgets(line, 1024, fp) == 0) {
733            fclose(fp);
734            return -1;
735        }
736    } while (strncmp(compare, line, len));
737
738    // we have the line, now increment the line ptr to the value
739    char* ptr = line + len;
740    jint result = atoi(ptr);
741    fclose(fp);
742    return result;
743}
744
745static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
746{
747    return read_binder_stat("bcTRANSACTION");
748}
749
750static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
751{
752    return read_binder_stat("brTRANSACTION");
753}
754
755// these are implemented in android_util_Binder.cpp
756jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
757jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
758jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
759
760
761/* pulled out of bionic */
762extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
763    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
764extern "C" void free_malloc_leak_info(uint8_t* info);
765#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
766#define BACKTRACE_SIZE          32
767
768/*
769 * This is a qsort() callback.
770 *
771 * See dumpNativeHeap() for comments about the data format and sort order.
772 */
773static int compareHeapRecords(const void* vrec1, const void* vrec2)
774{
775    const size_t* rec1 = (const size_t*) vrec1;
776    const size_t* rec2 = (const size_t*) vrec2;
777    size_t size1 = *rec1;
778    size_t size2 = *rec2;
779
780    if (size1 < size2) {
781        return 1;
782    } else if (size1 > size2) {
783        return -1;
784    }
785
786    intptr_t* bt1 = (intptr_t*)(rec1 + 2);
787    intptr_t* bt2 = (intptr_t*)(rec2 + 2);
788    for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
789        intptr_t addr1 = bt1[idx];
790        intptr_t addr2 = bt2[idx];
791        if (addr1 == addr2) {
792            if (addr1 == 0)
793                break;
794            continue;
795        }
796        if (addr1 < addr2) {
797            return -1;
798        } else if (addr1 > addr2) {
799            return 1;
800        }
801    }
802
803    return 0;
804}
805
806/*
807 * The get_malloc_leak_info() call returns an array of structs that
808 * look like this:
809 *
810 *   size_t size
811 *   size_t allocations
812 *   intptr_t backtrace[32]
813 *
814 * "size" is the size of the allocation, "backtrace" is a fixed-size
815 * array of function pointers, and "allocations" is the number of
816 * allocations with the exact same size and backtrace.
817 *
818 * The entries are sorted by descending total size (i.e. size*allocations)
819 * then allocation count.  For best results with "diff" we'd like to sort
820 * primarily by individual size then stack trace.  Since the entries are
821 * fixed-size, and we're allowed (by the current implementation) to mangle
822 * them, we can do this in place.
823 */
824static void dumpNativeHeap(FILE* fp)
825{
826    uint8_t* info = NULL;
827    size_t overallSize, infoSize, totalMemory, backtraceSize;
828
829    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
830        &backtraceSize);
831    if (info == NULL) {
832        fprintf(fp, "Native heap dump not available. To enable, run these"
833                    " commands (requires root):\n");
834        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
835        fprintf(fp, "$ adb shell stop\n");
836        fprintf(fp, "$ adb shell start\n");
837        return;
838    }
839    assert(infoSize != 0);
840    assert(overallSize % infoSize == 0);
841
842    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
843
844    size_t recordCount = overallSize / infoSize;
845    fprintf(fp, "Total memory: %zu\n", totalMemory);
846    fprintf(fp, "Allocation records: %zd\n", recordCount);
847    if (backtraceSize != BACKTRACE_SIZE) {
848        fprintf(fp, "WARNING: mismatched backtrace sizes (%zu vs. %d)\n",
849            backtraceSize, BACKTRACE_SIZE);
850    }
851    fprintf(fp, "\n");
852
853    /* re-sort the entries */
854    qsort(info, recordCount, infoSize, compareHeapRecords);
855
856    /* dump the entries to the file */
857    const uint8_t* ptr = info;
858    for (size_t idx = 0; idx < recordCount; idx++) {
859        size_t size = *(size_t*) ptr;
860        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
861        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
862
863        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
864                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
865                size & ~SIZE_FLAG_ZYGOTE_CHILD,
866                allocations);
867        for (size_t bt = 0; bt < backtraceSize; bt++) {
868            if (backtrace[bt] == 0) {
869                break;
870            } else {
871#ifdef __LP64__
872                fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
873#else
874                fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
875#endif
876            }
877        }
878        fprintf(fp, "\n");
879
880        ptr += infoSize;
881    }
882
883    free_malloc_leak_info(info);
884
885    fprintf(fp, "MAPS\n");
886    const char* maps = "/proc/self/maps";
887    FILE* in = fopen(maps, "r");
888    if (in == NULL) {
889        fprintf(fp, "Could not open %s\n", maps);
890        return;
891    }
892    char buf[BUFSIZ];
893    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
894        fwrite(buf, sizeof(char), n, fp);
895    }
896    fclose(in);
897
898    fprintf(fp, "END\n");
899}
900
901/*
902 * Dump the native heap, writing human-readable output to the specified
903 * file descriptor.
904 */
905static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
906    jobject fileDescriptor)
907{
908    if (fileDescriptor == NULL) {
909        jniThrowNullPointerException(env, "fd == null");
910        return;
911    }
912    int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
913    if (origFd < 0) {
914        jniThrowRuntimeException(env, "Invalid file descriptor");
915        return;
916    }
917
918    /* dup() the descriptor so we don't close the original with fclose() */
919    int fd = dup(origFd);
920    if (fd < 0) {
921        ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
922        jniThrowRuntimeException(env, "dup() failed");
923        return;
924    }
925
926    FILE* fp = fdopen(fd, "w");
927    if (fp == NULL) {
928        ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
929        close(fd);
930        jniThrowRuntimeException(env, "fdopen() failed");
931        return;
932    }
933
934    ALOGD("Native heap dump starting...\n");
935    dumpNativeHeap(fp);
936    ALOGD("Native heap dump complete.\n");
937
938    fclose(fp);
939}
940
941
942static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz,
943    jint pid, jstring fileName)
944{
945    if (fileName == NULL) {
946        jniThrowNullPointerException(env, "file == null");
947        return;
948    }
949    const jchar* str = env->GetStringCritical(fileName, 0);
950    String8 fileName8;
951    if (str) {
952        fileName8 = String8(reinterpret_cast<const char16_t*>(str),
953                            env->GetStringLength(fileName));
954        env->ReleaseStringCritical(fileName, str);
955    }
956
957    int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
958    if (fd < 0) {
959        fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
960        return;
961    }
962
963    if (lseek(fd, 0, SEEK_END) < 0) {
964        fprintf(stderr, "lseek: %s\n", strerror(errno));
965    } else {
966        dump_backtrace_to_file(pid, fd);
967    }
968
969    close(fd);
970}
971
972/*
973 * JNI registration.
974 */
975
976static const JNINativeMethod gMethods[] = {
977    { "getNativeHeapSize",      "()J",
978            (void*) android_os_Debug_getNativeHeapSize },
979    { "getNativeHeapAllocatedSize", "()J",
980            (void*) android_os_Debug_getNativeHeapAllocatedSize },
981    { "getNativeHeapFreeSize",  "()J",
982            (void*) android_os_Debug_getNativeHeapFreeSize },
983    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
984            (void*) android_os_Debug_getDirtyPages },
985    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
986            (void*) android_os_Debug_getDirtyPagesPid },
987    { "getPss",                 "()J",
988            (void*) android_os_Debug_getPss },
989    { "getPss",                 "(I[J[J)J",
990            (void*) android_os_Debug_getPssPid },
991    { "getMemInfo",             "([J)V",
992            (void*) android_os_Debug_getMemInfo },
993    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
994            (void*) android_os_Debug_dumpNativeHeap },
995    { "getBinderSentTransactions", "()I",
996            (void*) android_os_Debug_getBinderSentTransactions },
997    { "getBinderReceivedTransactions", "()I",
998            (void*) android_os_getBinderReceivedTransactions },
999    { "getBinderLocalObjectCount", "()I",
1000            (void*)android_os_Debug_getLocalObjectCount },
1001    { "getBinderProxyObjectCount", "()I",
1002            (void*)android_os_Debug_getProxyObjectCount },
1003    { "getBinderDeathObjectCount", "()I",
1004            (void*)android_os_Debug_getDeathObjectCount },
1005    { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V",
1006            (void*)android_os_Debug_dumpNativeBacktraceToFile },
1007};
1008
1009int register_android_os_Debug(JNIEnv *env)
1010{
1011    int err = memtrack_init();
1012    if (err != 0) {
1013        memtrackLoaded = false;
1014        ALOGE("failed to load memtrack module: %d", err);
1015    } else {
1016        memtrackLoaded = true;
1017    }
1018
1019    jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
1020
1021    // Sanity check the number of other statistics expected in Java matches here.
1022    jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
1023    jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
1024    jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
1025    jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
1026    int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
1027    if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
1028        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
1029                             "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
1030                             numOtherStats+numDvkStats, expectedNumOtherStats);
1031        return JNI_ERR;
1032    }
1033
1034    otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
1035
1036    for (int i=0; i<_NUM_CORE_HEAP; i++) {
1037        stat_fields[i].pss_field =
1038                env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
1039        stat_fields[i].pssSwappable_field =
1040                env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
1041        stat_fields[i].privateDirty_field =
1042                env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
1043        stat_fields[i].sharedDirty_field =
1044                env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
1045        stat_fields[i].privateClean_field =
1046                env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
1047        stat_fields[i].sharedClean_field =
1048                env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
1049        stat_fields[i].swappedOut_field =
1050                env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
1051    }
1052
1053    return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
1054}
1055
1056}; // namespace android
1057