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