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