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