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