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