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