android_os_Debug.cpp revision 1b45fcd14ebcbc3de54120a05634303b0161e497
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
19#include <assert.h>
20#include <ctype.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <inttypes.h>
24#include <malloc.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/time.h>
29#include <time.h>
30#include <unistd.h>
31
32#include <iomanip>
33#include <string>
34
35#include <android-base/stringprintf.h>
36#include <android-base/unique_fd.h>
37#include <debuggerd/client.h>
38#include <log/log.h>
39#include <utils/misc.h>
40#include <utils/String8.h>
41
42#include "JNIHelp.h"
43#include "ScopedUtfChars.h"
44#include "jni.h"
45#include <memtrack/memtrack.h>
46#include <memunreachable/memunreachable.h>
47
48namespace android
49{
50
51static void safeFclose(FILE* fp) {
52  if (fp) fclose(fp);
53}
54
55using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>;
56
57static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
58    return UniqueFile(fopen(path, mode), safeFclose);
59}
60
61enum {
62    HEAP_UNKNOWN,
63    HEAP_DALVIK,
64    HEAP_NATIVE,
65
66    HEAP_DALVIK_OTHER,
67    HEAP_STACK,
68    HEAP_CURSOR,
69    HEAP_ASHMEM,
70    HEAP_GL_DEV,
71    HEAP_UNKNOWN_DEV,
72    HEAP_SO,
73    HEAP_JAR,
74    HEAP_APK,
75    HEAP_TTF,
76    HEAP_DEX,
77    HEAP_OAT,
78    HEAP_ART,
79    HEAP_UNKNOWN_MAP,
80    HEAP_GRAPHICS,
81    HEAP_GL,
82    HEAP_OTHER_MEMTRACK,
83
84    // Dalvik extra sections (heap).
85    HEAP_DALVIK_NORMAL,
86    HEAP_DALVIK_LARGE,
87    HEAP_DALVIK_ZYGOTE,
88    HEAP_DALVIK_NON_MOVING,
89
90    // Dalvik other extra sections.
91    HEAP_DALVIK_OTHER_LINEARALLOC,
92    HEAP_DALVIK_OTHER_ACCOUNTING,
93    HEAP_DALVIK_OTHER_CODE_CACHE,
94    HEAP_DALVIK_OTHER_COMPILER_METADATA,
95    HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE,
96
97    // Boot vdex / app dex / app vdex
98    HEAP_DEX_BOOT_VDEX,
99    HEAP_DEX_APP_DEX,
100    HEAP_DEX_APP_VDEX,
101
102    // App art, boot art.
103    HEAP_ART_APP,
104    HEAP_ART_BOOT,
105
106    _NUM_HEAP,
107    _NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1,
108    _NUM_CORE_HEAP = HEAP_NATIVE+1
109};
110
111struct stat_fields {
112    jfieldID pss_field;
113    jfieldID pssSwappable_field;
114    jfieldID privateDirty_field;
115    jfieldID sharedDirty_field;
116    jfieldID privateClean_field;
117    jfieldID sharedClean_field;
118    jfieldID swappedOut_field;
119    jfieldID swappedOutPss_field;
120};
121
122struct stat_field_names {
123    const char* pss_name;
124    const char* pssSwappable_name;
125    const char* privateDirty_name;
126    const char* sharedDirty_name;
127    const char* privateClean_name;
128    const char* sharedClean_name;
129    const char* swappedOut_name;
130    const char* swappedOutPss_name;
131};
132
133static stat_fields stat_fields[_NUM_CORE_HEAP];
134
135static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
136    { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty",
137        "otherPrivateClean", "otherSharedClean", "otherSwappedOut", "otherSwappedOutPss" },
138    { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty",
139        "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut", "dalvikSwappedOutPss" },
140    { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty",
141        "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
142};
143
144jfieldID otherStats_field;
145jfieldID hasSwappedOutPss_field;
146
147struct stats_t {
148    int pss;
149    int swappablePss;
150    int privateDirty;
151    int sharedDirty;
152    int privateClean;
153    int sharedClean;
154    int swappedOut;
155    int swappedOutPss;
156};
157
158#define BINDER_STATS "/proc/binder/stats"
159
160static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
161{
162    struct mallinfo info = mallinfo();
163    return (jlong) info.usmblks;
164}
165
166static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
167{
168    struct mallinfo info = mallinfo();
169    return (jlong) info.uordblks;
170}
171
172static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
173{
174    struct mallinfo info = mallinfo();
175    return (jlong) info.fordblks;
176}
177
178// Container used to retrieve graphics memory pss
179struct graphics_memory_pss
180{
181    int graphics;
182    int gl;
183    int other;
184};
185
186/*
187 * Uses libmemtrack to retrieve graphics memory that the process is using.
188 * Any graphics memory reported in /proc/pid/smaps is not included here.
189 */
190static int read_memtrack_memory(struct memtrack_proc* p, int pid,
191        struct graphics_memory_pss* graphics_mem)
192{
193    int err = memtrack_proc_get(p, pid);
194    if (err != 0) {
195        ALOGW("failed to get memory consumption info: %d", err);
196        return err;
197    }
198
199    ssize_t pss = memtrack_proc_graphics_pss(p);
200    if (pss < 0) {
201        ALOGW("failed to get graphics pss: %zd", pss);
202        return pss;
203    }
204    graphics_mem->graphics = pss / 1024;
205
206    pss = memtrack_proc_gl_pss(p);
207    if (pss < 0) {
208        ALOGW("failed to get gl pss: %zd", pss);
209        return pss;
210    }
211    graphics_mem->gl = pss / 1024;
212
213    pss = memtrack_proc_other_pss(p);
214    if (pss < 0) {
215        ALOGW("failed to get other pss: %zd", pss);
216        return pss;
217    }
218    graphics_mem->other = pss / 1024;
219
220    return 0;
221}
222
223/*
224 * Retrieves the graphics memory that is unaccounted for in /proc/pid/smaps.
225 */
226static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_mem)
227{
228    struct memtrack_proc* p = memtrack_proc_new();
229    if (p == NULL) {
230        ALOGW("failed to create memtrack_proc");
231        return -1;
232    }
233
234    int err = read_memtrack_memory(p, pid, graphics_mem);
235    memtrack_proc_destroy(p);
236    return err;
237}
238
239static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
240{
241    char line[1024];
242    int len, nameLen;
243    bool skip, done = false;
244
245    unsigned pss = 0, swappable_pss = 0;
246    float sharing_proportion = 0.0;
247    unsigned shared_clean = 0, shared_dirty = 0;
248    unsigned private_clean = 0, private_dirty = 0;
249    unsigned swapped_out = 0, swapped_out_pss = 0;
250    bool is_swappable = false;
251    unsigned temp;
252
253    uint64_t start;
254    uint64_t end = 0;
255    uint64_t prevEnd = 0;
256    char* name;
257    int name_pos;
258
259    int whichHeap = HEAP_UNKNOWN;
260    int subHeap = HEAP_UNKNOWN;
261    int prevHeap = HEAP_UNKNOWN;
262
263    *foundSwapPss = false;
264
265    if(fgets(line, sizeof(line), fp) == 0) return;
266
267    while (!done) {
268        prevHeap = whichHeap;
269        prevEnd = end;
270        whichHeap = HEAP_UNKNOWN;
271        subHeap = HEAP_UNKNOWN;
272        skip = false;
273        is_swappable = false;
274
275        len = strlen(line);
276        if (len < 1) return;
277        line[--len] = 0;
278
279        if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
280            skip = true;
281        } else {
282            while (isspace(line[name_pos])) {
283                name_pos += 1;
284            }
285            name = line + name_pos;
286            nameLen = strlen(name);
287            // Trim the end of the line if it is " (deleted)".
288            const char* deleted_str = " (deleted)";
289            if (nameLen > (int)strlen(deleted_str) &&
290                strcmp(name+nameLen-strlen(deleted_str), deleted_str) == 0) {
291                nameLen -= strlen(deleted_str);
292                name[nameLen] = '\0';
293            }
294            if ((strstr(name, "[heap]") == name)) {
295                whichHeap = HEAP_NATIVE;
296            } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) {
297                whichHeap = HEAP_NATIVE;
298            } else if (strncmp(name, "[stack", 6) == 0) {
299                whichHeap = HEAP_STACK;
300            } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
301                whichHeap = HEAP_SO;
302                is_swappable = true;
303            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
304                whichHeap = HEAP_JAR;
305                is_swappable = true;
306            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
307                whichHeap = HEAP_APK;
308                is_swappable = true;
309            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
310                whichHeap = HEAP_TTF;
311                is_swappable = true;
312            } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) ||
313                       (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
314                whichHeap = HEAP_DEX;
315                subHeap = HEAP_DEX_APP_DEX;
316                is_swappable = true;
317            } else if (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0) {
318                whichHeap = HEAP_DEX;
319                // Handle system@framework@boot* and system/framework/boot*
320                if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
321                    subHeap = HEAP_DEX_BOOT_VDEX;
322                } else {
323                    subHeap = HEAP_DEX_APP_VDEX;
324                }
325                is_swappable = true;
326            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
327                whichHeap = HEAP_OAT;
328                is_swappable = true;
329            } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
330                whichHeap = HEAP_ART;
331                // Handle system@framework@boot* and system/framework/boot*
332                if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
333                    subHeap = HEAP_ART_BOOT;
334                } else {
335                    subHeap = HEAP_ART_APP;
336                }
337                is_swappable = true;
338            } else if (strncmp(name, "/dev/", 5) == 0) {
339                if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) {
340                    whichHeap = HEAP_GL_DEV;
341                } else if (strncmp(name, "/dev/ashmem", 11) == 0) {
342                    if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) {
343                        whichHeap = HEAP_DALVIK_OTHER;
344                        if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) {
345                            subHeap = HEAP_DALVIK_OTHER_LINEARALLOC;
346                        } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) ||
347                                   (strstr(name, "/dev/ashmem/dalvik-main space") == name)) {
348                            // This is the regular Dalvik heap.
349                            whichHeap = HEAP_DALVIK;
350                            subHeap = HEAP_DALVIK_NORMAL;
351                        } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name ||
352                                   strstr(name, "/dev/ashmem/dalvik-free list large object space")
353                                       == name) {
354                            whichHeap = HEAP_DALVIK;
355                            subHeap = HEAP_DALVIK_LARGE;
356                        } else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) {
357                            whichHeap = HEAP_DALVIK;
358                            subHeap = HEAP_DALVIK_NON_MOVING;
359                        } else if (strstr(name, "/dev/ashmem/dalvik-zygote space") == name) {
360                            whichHeap = HEAP_DALVIK;
361                            subHeap = HEAP_DALVIK_ZYGOTE;
362                        } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) {
363                            subHeap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
364                        } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name ||
365                                   strstr(name, "/dev/ashmem/dalvik-data-code-cache") == name) {
366                            subHeap = HEAP_DALVIK_OTHER_CODE_CACHE;
367                        } else if (strstr(name, "/dev/ashmem/dalvik-CompilerMetadata") == name) {
368                            subHeap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
369                        } else {
370                            subHeap = HEAP_DALVIK_OTHER_ACCOUNTING;  // Default to accounting.
371                        }
372                    } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
373                        whichHeap = HEAP_CURSOR;
374                    } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) {
375                        whichHeap = HEAP_NATIVE;
376                    } else {
377                        whichHeap = HEAP_ASHMEM;
378                    }
379                } else {
380                    whichHeap = HEAP_UNKNOWN_DEV;
381                }
382            } else if (strncmp(name, "[anon:", 6) == 0) {
383                whichHeap = HEAP_UNKNOWN;
384            } else if (nameLen > 0) {
385                whichHeap = HEAP_UNKNOWN_MAP;
386            } else if (start == prevEnd && prevHeap == HEAP_SO) {
387                // bss section of a shared library.
388                whichHeap = HEAP_SO;
389            }
390        }
391
392        //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
393        //    isSqliteHeap, line);
394
395        shared_clean = 0;
396        shared_dirty = 0;
397        private_clean = 0;
398        private_dirty = 0;
399        swapped_out = 0;
400        swapped_out_pss = 0;
401
402        while (true) {
403            if (fgets(line, 1024, fp) == 0) {
404                done = true;
405                break;
406            }
407
408            if (line[0] == 'S' && sscanf(line, "Size: %d kB", &temp) == 1) {
409                /* size = temp; */
410            } else if (line[0] == 'R' && sscanf(line, "Rss: %d kB", &temp) == 1) {
411                /* resident = temp; */
412            } else if (line[0] == 'P' && sscanf(line, "Pss: %d kB", &temp) == 1) {
413                pss = temp;
414            } else if (line[0] == 'S' && sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
415                shared_clean = temp;
416            } else if (line[0] == 'S' && sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
417                shared_dirty = temp;
418            } else if (line[0] == 'P' && sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
419                private_clean = temp;
420            } else if (line[0] == 'P' && sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
421                private_dirty = temp;
422            } else if (line[0] == 'R' && sscanf(line, "Referenced: %d kB", &temp) == 1) {
423                /* referenced = temp; */
424            } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
425                swapped_out = temp;
426            } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) {
427                *foundSwapPss = true;
428                swapped_out_pss = temp;
429            } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
430                // looks like a new mapping
431                // example: "10000000-10001000 ---p 10000000 00:00 0"
432                break;
433            }
434        }
435
436        if (!skip) {
437            if (is_swappable && (pss > 0)) {
438                sharing_proportion = 0.0;
439                if ((shared_clean > 0) || (shared_dirty > 0)) {
440                    sharing_proportion = (pss - private_clean
441                            - private_dirty)/(shared_clean+shared_dirty);
442                }
443                swappable_pss = (sharing_proportion*shared_clean) + private_clean;
444            } else
445                swappable_pss = 0;
446
447            stats[whichHeap].pss += pss;
448            stats[whichHeap].swappablePss += swappable_pss;
449            stats[whichHeap].privateDirty += private_dirty;
450            stats[whichHeap].sharedDirty += shared_dirty;
451            stats[whichHeap].privateClean += private_clean;
452            stats[whichHeap].sharedClean += shared_clean;
453            stats[whichHeap].swappedOut += swapped_out;
454            stats[whichHeap].swappedOutPss += swapped_out_pss;
455            if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER ||
456                    whichHeap == HEAP_DEX || whichHeap == HEAP_ART) {
457                stats[subHeap].pss += pss;
458                stats[subHeap].swappablePss += swappable_pss;
459                stats[subHeap].privateDirty += private_dirty;
460                stats[subHeap].sharedDirty += shared_dirty;
461                stats[subHeap].privateClean += private_clean;
462                stats[subHeap].sharedClean += shared_clean;
463                stats[subHeap].swappedOut += swapped_out;
464                stats[subHeap].swappedOutPss += swapped_out_pss;
465            }
466        }
467    }
468}
469
470static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
471{
472    *foundSwapPss = false;
473
474    std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
475    UniqueFile fp = MakeUniqueFile(smaps_path.c_str(), "re");
476    if (fp == nullptr) return;
477
478    read_mapinfo(fp.get(), stats, foundSwapPss);
479}
480
481static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
482        jint pid, jobject object)
483{
484    bool foundSwapPss;
485    stats_t stats[_NUM_HEAP];
486    memset(&stats, 0, sizeof(stats));
487
488    load_maps(pid, stats, &foundSwapPss);
489
490    struct graphics_memory_pss graphics_mem;
491    if (read_memtrack_memory(pid, &graphics_mem) == 0) {
492        stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
493        stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
494        stats[HEAP_GL].pss = graphics_mem.gl;
495        stats[HEAP_GL].privateDirty = graphics_mem.gl;
496        stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
497        stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
498    }
499
500    for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
501        stats[HEAP_UNKNOWN].pss += stats[i].pss;
502        stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
503        stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
504        stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
505        stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
506        stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
507        stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
508        stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
509    }
510
511    for (int i=0; i<_NUM_CORE_HEAP; i++) {
512        env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
513        env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
514        env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
515        env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
516        env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
517        env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
518        env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
519        env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
520    }
521
522
523    env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
524    jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
525
526    jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
527    if (otherArray == NULL) {
528        return;
529    }
530
531    int j=0;
532    for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
533        otherArray[j++] = stats[i].pss;
534        otherArray[j++] = stats[i].swappablePss;
535        otherArray[j++] = stats[i].privateDirty;
536        otherArray[j++] = stats[i].sharedDirty;
537        otherArray[j++] = stats[i].privateClean;
538        otherArray[j++] = stats[i].sharedClean;
539        otherArray[j++] = stats[i].swappedOut;
540        otherArray[j++] = stats[i].swappedOutPss;
541    }
542
543    env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
544}
545
546static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
547{
548    android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
549}
550
551static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
552        jlongArray outUssSwapPss, jlongArray outMemtrack)
553{
554    char line[1024];
555    jlong pss = 0;
556    jlong swapPss = 0;
557    jlong uss = 0;
558    jlong memtrack = 0;
559
560    struct graphics_memory_pss graphics_mem;
561    if (read_memtrack_memory(pid, &graphics_mem) == 0) {
562        pss = uss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
563    }
564
565    {
566        std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
567        UniqueFile fp = MakeUniqueFile(smaps_path.c_str(), "re");
568
569        if (fp != nullptr) {
570            while (true) {
571                if (fgets(line, 1024, fp.get()) == NULL) {
572                    break;
573                }
574
575                if (line[0] == 'P') {
576                    if (strncmp(line, "Pss:", 4) == 0) {
577                        char* c = line + 4;
578                        while (*c != 0 && (*c < '0' || *c > '9')) {
579                            c++;
580                        }
581                        pss += atoi(c);
582                    } else if (strncmp(line, "Private_Clean:", 14) == 0
583                                || strncmp(line, "Private_Dirty:", 14) == 0) {
584                        char* c = line + 14;
585                        while (*c != 0 && (*c < '0' || *c > '9')) {
586                            c++;
587                        }
588                        uss += atoi(c);
589                    }
590                } else if (line[0] == 'S' && strncmp(line, "SwapPss:", 8) == 0) {
591                    char* c = line + 8;
592                    jlong lSwapPss;
593                    while (*c != 0 && (*c < '0' || *c > '9')) {
594                        c++;
595                    }
596                    lSwapPss = atoi(c);
597                    swapPss += lSwapPss;
598                    pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
599                }
600            }
601        }
602    }
603
604    if (outUssSwapPss != NULL) {
605        if (env->GetArrayLength(outUssSwapPss) >= 1) {
606            jlong* outUssSwapPssArray = env->GetLongArrayElements(outUssSwapPss, 0);
607            if (outUssSwapPssArray != NULL) {
608                outUssSwapPssArray[0] = uss;
609                if (env->GetArrayLength(outUssSwapPss) >= 2) {
610                    outUssSwapPssArray[1] = swapPss;
611                }
612            }
613            env->ReleaseLongArrayElements(outUssSwapPss, outUssSwapPssArray, 0);
614        }
615    }
616
617    if (outMemtrack != NULL) {
618        if (env->GetArrayLength(outMemtrack) >= 1) {
619            jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
620            if (outMemtrackArray != NULL) {
621                outMemtrackArray[0] = memtrack;
622            }
623            env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
624        }
625    }
626
627    return pss;
628}
629
630static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
631{
632    return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
633}
634
635static long get_allocated_vmalloc_memory() {
636    char line[1024];
637    // Ignored tags that don't actually consume memory (ie remappings)
638    static const char* const ignored_tags[] = {
639            "ioremap",
640            "map_lowmem",
641            "vm_map_ram",
642            NULL
643    };
644    long size, vmalloc_allocated_size = 0;
645
646    UniqueFile fp = MakeUniqueFile("/proc/vmallocinfo", "re");
647    if (fp == nullptr) {
648        return 0;
649    }
650
651    while (true) {
652        if (fgets(line, 1024, fp.get()) == NULL) {
653            break;
654        }
655        bool valid_line = true;
656        int i = 0;
657        while (ignored_tags[i]) {
658            if (strstr(line, ignored_tags[i]) != NULL) {
659                valid_line = false;
660                break;
661            }
662            i++;
663        }
664        if (valid_line && (sscanf(line, "%*x-%*x %ld", &size) == 1)) {
665            vmalloc_allocated_size += size;
666        }
667    }
668    return vmalloc_allocated_size;
669}
670
671enum {
672    MEMINFO_TOTAL,
673    MEMINFO_FREE,
674    MEMINFO_BUFFERS,
675    MEMINFO_CACHED,
676    MEMINFO_SHMEM,
677    MEMINFO_SLAB,
678    MEMINFO_SWAP_TOTAL,
679    MEMINFO_SWAP_FREE,
680    MEMINFO_ZRAM_TOTAL,
681    MEMINFO_MAPPED,
682    MEMINFO_VMALLOC_USED,
683    MEMINFO_PAGE_TABLES,
684    MEMINFO_KERNEL_STACK,
685    MEMINFO_COUNT
686};
687
688static long long get_zram_mem_used()
689{
690#define ZRAM_SYSFS "/sys/block/zram0/"
691    UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
692    if (mm_stat_file) {
693        long long mem_used_total = 0;
694
695        int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
696        if (matched != 1)
697            ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
698
699        return mem_used_total;
700    }
701
702    UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
703    if (mem_used_total_file) {
704        long long mem_used_total = 0;
705
706        int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
707        if (matched != 1)
708            ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
709
710        return mem_used_total;
711    }
712
713    return 0;
714}
715
716static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
717{
718    char buffer[1024];
719    size_t numFound = 0;
720
721    if (out == NULL) {
722        jniThrowNullPointerException(env, "out == null");
723        return;
724    }
725
726    int fd = open("/proc/meminfo", O_RDONLY);
727
728    if (fd < 0) {
729        ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
730        return;
731    }
732
733    int len = read(fd, buffer, sizeof(buffer)-1);
734    close(fd);
735
736    if (len < 0) {
737        ALOGW("Empty /proc/meminfo");
738        return;
739    }
740    buffer[len] = 0;
741
742    static const char* const tags[] = {
743            "MemTotal:",
744            "MemFree:",
745            "Buffers:",
746            "Cached:",
747            "Shmem:",
748            "Slab:",
749            "SwapTotal:",
750            "SwapFree:",
751            "ZRam:",
752            "Mapped:",
753            "VmallocUsed:",
754            "PageTables:",
755            "KernelStack:",
756            NULL
757    };
758    static const int tagsLen[] = {
759            9,
760            8,
761            8,
762            7,
763            6,
764            5,
765            10,
766            9,
767            5,
768            7,
769            12,
770            11,
771            12,
772            0
773    };
774    long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
775
776    char* p = buffer;
777    while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
778        int i = 0;
779        while (tags[i]) {
780            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
781                p += tagsLen[i];
782                while (*p == ' ') p++;
783                char* num = p;
784                while (*p >= '0' && *p <= '9') p++;
785                if (*p != 0) {
786                    *p = 0;
787                    p++;
788                }
789                mem[i] = atoll(num);
790                numFound++;
791                break;
792            }
793            i++;
794        }
795        while (*p && *p != '\n') {
796            p++;
797        }
798        if (*p) p++;
799    }
800
801    mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
802    // Recompute Vmalloc Used since the value in meminfo
803    // doesn't account for I/O remapping which doesn't use RAM.
804    mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
805
806    int maxNum = env->GetArrayLength(out);
807    if (maxNum > MEMINFO_COUNT) {
808        maxNum = MEMINFO_COUNT;
809    }
810    jlong* outArray = env->GetLongArrayElements(out, 0);
811    if (outArray != NULL) {
812        for (int i=0; i<maxNum; i++) {
813            outArray[i] = mem[i];
814        }
815    }
816    env->ReleaseLongArrayElements(out, outArray, 0);
817}
818
819
820static jint read_binder_stat(const char* stat)
821{
822    UniqueFile fp = MakeUniqueFile(BINDER_STATS, "re");
823    if (fp == nullptr) {
824        return -1;
825    }
826
827    char line[1024];
828
829    char compare[128];
830    int len = snprintf(compare, 128, "proc %d", getpid());
831
832    // loop until we have the block that represents this process
833    do {
834        if (fgets(line, 1024, fp.get()) == 0) {
835            return -1;
836        }
837    } while (strncmp(compare, line, len));
838
839    // now that we have this process, read until we find the stat that we are looking for
840    len = snprintf(compare, 128, "  %s: ", stat);
841
842    do {
843        if (fgets(line, 1024, fp.get()) == 0) {
844            return -1;
845        }
846    } while (strncmp(compare, line, len));
847
848    // we have the line, now increment the line ptr to the value
849    char* ptr = line + len;
850    jint result = atoi(ptr);
851    return result;
852}
853
854static jint android_os_Debug_getBinderSentTransactions(JNIEnv *env, jobject clazz)
855{
856    return read_binder_stat("bcTRANSACTION");
857}
858
859static jint android_os_getBinderReceivedTransactions(JNIEnv *env, jobject clazz)
860{
861    return read_binder_stat("brTRANSACTION");
862}
863
864// these are implemented in android_util_Binder.cpp
865jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz);
866jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
867jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
868
869
870/* pulled out of bionic */
871extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
872    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
873extern "C" void free_malloc_leak_info(uint8_t* info);
874#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
875
876static size_t gNumBacktraceElements;
877
878/*
879 * This is a qsort() callback.
880 *
881 * See dumpNativeHeap() for comments about the data format and sort order.
882 */
883static int compareHeapRecords(const void* vrec1, const void* vrec2)
884{
885    const size_t* rec1 = (const size_t*) vrec1;
886    const size_t* rec2 = (const size_t*) vrec2;
887    size_t size1 = *rec1;
888    size_t size2 = *rec2;
889
890    if (size1 < size2) {
891        return 1;
892    } else if (size1 > size2) {
893        return -1;
894    }
895
896    uintptr_t* bt1 = (uintptr_t*)(rec1 + 2);
897    uintptr_t* bt2 = (uintptr_t*)(rec2 + 2);
898    for (size_t idx = 0; idx < gNumBacktraceElements; idx++) {
899        uintptr_t addr1 = bt1[idx];
900        uintptr_t addr2 = bt2[idx];
901        if (addr1 == addr2) {
902            if (addr1 == 0)
903                break;
904            continue;
905        }
906        if (addr1 < addr2) {
907            return -1;
908        } else if (addr1 > addr2) {
909            return 1;
910        }
911    }
912
913    return 0;
914}
915
916/*
917 * The get_malloc_leak_info() call returns an array of structs that
918 * look like this:
919 *
920 *   size_t size
921 *   size_t allocations
922 *   intptr_t backtrace[32]
923 *
924 * "size" is the size of the allocation, "backtrace" is a fixed-size
925 * array of function pointers, and "allocations" is the number of
926 * allocations with the exact same size and backtrace.
927 *
928 * The entries are sorted by descending total size (i.e. size*allocations)
929 * then allocation count.  For best results with "diff" we'd like to sort
930 * primarily by individual size then stack trace.  Since the entries are
931 * fixed-size, and we're allowed (by the current implementation) to mangle
932 * them, we can do this in place.
933 */
934static void dumpNativeHeap(FILE* fp)
935{
936    uint8_t* info = NULL;
937    size_t overallSize, infoSize, totalMemory, backtraceSize;
938
939    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
940        &backtraceSize);
941    if (info == NULL) {
942        fprintf(fp, "Native heap dump not available. To enable, run these"
943                    " commands (requires root):\n");
944        fprintf(fp, "# adb shell stop\n");
945        fprintf(fp, "# adb shell setprop libc.debug.malloc.options "
946                    "backtrace\n");
947        fprintf(fp, "# adb shell start\n");
948        return;
949    }
950    assert(infoSize != 0);
951    assert(overallSize % infoSize == 0);
952
953    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
954
955    size_t recordCount = overallSize / infoSize;
956    fprintf(fp, "Total memory: %zu\n", totalMemory);
957    fprintf(fp, "Allocation records: %zd\n", recordCount);
958    fprintf(fp, "Backtrace size: %zd\n", backtraceSize);
959    fprintf(fp, "\n");
960
961    /* re-sort the entries */
962    gNumBacktraceElements = backtraceSize;
963    qsort(info, recordCount, infoSize, compareHeapRecords);
964
965    /* dump the entries to the file */
966    const uint8_t* ptr = info;
967    for (size_t idx = 0; idx < recordCount; idx++) {
968        size_t size = *(size_t*) ptr;
969        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
970        uintptr_t* backtrace = (uintptr_t*) (ptr + sizeof(size_t) * 2);
971
972        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
973                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
974                size & ~SIZE_FLAG_ZYGOTE_CHILD,
975                allocations);
976        for (size_t bt = 0; bt < backtraceSize; bt++) {
977            if (backtrace[bt] == 0) {
978                break;
979            } else {
980#ifdef __LP64__
981                fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
982#else
983                fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
984#endif
985            }
986        }
987        fprintf(fp, "\n");
988
989        ptr += infoSize;
990    }
991
992    free_malloc_leak_info(info);
993
994    fprintf(fp, "MAPS\n");
995    const char* maps = "/proc/self/maps";
996    UniqueFile in = MakeUniqueFile(maps, "re");
997    if (in == nullptr) {
998        fprintf(fp, "Could not open %s\n", maps);
999        return;
1000    }
1001    char buf[BUFSIZ];
1002    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in.get())) {
1003        fwrite(buf, sizeof(char), n, fp);
1004    }
1005
1006    fprintf(fp, "END\n");
1007}
1008
1009static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
1010{
1011    if (fileDescriptor == NULL) {
1012        jniThrowNullPointerException(env, "fd == null");
1013        return false;
1014    }
1015    int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
1016    if (origFd < 0) {
1017        jniThrowRuntimeException(env, "Invalid file descriptor");
1018        return false;
1019    }
1020
1021    /* dup() the descriptor so we don't close the original with fclose() */
1022    int fd = dup(origFd);
1023    if (fd < 0) {
1024        ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
1025        jniThrowRuntimeException(env, "dup() failed");
1026        return false;
1027    }
1028
1029    fp.reset(fdopen(fd, "w"));
1030    if (fp == nullptr) {
1031        ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
1032        close(fd);
1033        jniThrowRuntimeException(env, "fdopen() failed");
1034        return false;
1035    }
1036    return true;
1037}
1038
1039/*
1040 * Dump the native heap, writing human-readable output to the specified
1041 * file descriptor.
1042 */
1043static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject,
1044    jobject fileDescriptor)
1045{
1046    UniqueFile fp(nullptr, safeFclose);
1047    if (!openFile(env, fileDescriptor, fp)) {
1048        return;
1049    }
1050
1051    ALOGD("Native heap dump starting...\n");
1052    dumpNativeHeap(fp.get());
1053    ALOGD("Native heap dump complete.\n");
1054}
1055
1056/*
1057 * Dump the native malloc info, writing xml output to the specified
1058 * file descriptor.
1059 */
1060static void android_os_Debug_dumpNativeMallocInfo(JNIEnv* env, jobject,
1061    jobject fileDescriptor)
1062{
1063    UniqueFile fp(nullptr, safeFclose);
1064    if (!openFile(env, fileDescriptor, fp)) {
1065        return;
1066    }
1067
1068    malloc_info(0, fp.get());
1069}
1070
1071static bool dumpTraces(JNIEnv* env, jint pid, jstring fileName, jint timeoutSecs,
1072                       DebuggerdDumpType dumpType) {
1073    const ScopedUtfChars fileNameChars(env, fileName);
1074    if (fileNameChars.c_str() == nullptr) {
1075        return false;
1076    }
1077
1078    android::base::unique_fd fd(open(fileNameChars.c_str(),
1079                                     O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND,
1080                                     0666));
1081    if (fd < 0) {
1082        fprintf(stderr, "Can't open %s: %s\n", fileNameChars.c_str(), strerror(errno));
1083        return false;
1084    }
1085
1086    return (dump_backtrace_to_file_timeout(pid, dumpType, timeoutSecs, fd) == 0);
1087}
1088
1089static jboolean android_os_Debug_dumpJavaBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
1090        jint pid, jstring fileName, jint timeoutSecs) {
1091    const bool ret =  dumpTraces(env, pid, fileName, timeoutSecs, kDebuggerdJavaBacktrace);
1092    return ret ? JNI_TRUE : JNI_FALSE;
1093}
1094
1095static jboolean android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv* env, jobject clazz,
1096        jint pid, jstring fileName, jint timeoutSecs) {
1097    const bool ret = dumpTraces(env, pid, fileName, timeoutSecs, kDebuggerdNativeBacktrace);
1098    return ret ? JNI_TRUE : JNI_FALSE;
1099}
1100
1101static jstring android_os_Debug_getUnreachableMemory(JNIEnv* env, jobject clazz,
1102    jint limit, jboolean contents)
1103{
1104    std::string s = GetUnreachableMemoryString(contents, limit);
1105    return env->NewStringUTF(s.c_str());
1106}
1107
1108/*
1109 * JNI registration.
1110 */
1111
1112static const JNINativeMethod gMethods[] = {
1113    { "getNativeHeapSize",      "()J",
1114            (void*) android_os_Debug_getNativeHeapSize },
1115    { "getNativeHeapAllocatedSize", "()J",
1116            (void*) android_os_Debug_getNativeHeapAllocatedSize },
1117    { "getNativeHeapFreeSize",  "()J",
1118            (void*) android_os_Debug_getNativeHeapFreeSize },
1119    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
1120            (void*) android_os_Debug_getDirtyPages },
1121    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
1122            (void*) android_os_Debug_getDirtyPagesPid },
1123    { "getPss",                 "()J",
1124            (void*) android_os_Debug_getPss },
1125    { "getPss",                 "(I[J[J)J",
1126            (void*) android_os_Debug_getPssPid },
1127    { "getMemInfo",             "([J)V",
1128            (void*) android_os_Debug_getMemInfo },
1129    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
1130            (void*) android_os_Debug_dumpNativeHeap },
1131    { "dumpNativeMallocInfo",   "(Ljava/io/FileDescriptor;)V",
1132            (void*) android_os_Debug_dumpNativeMallocInfo },
1133    { "getBinderSentTransactions", "()I",
1134            (void*) android_os_Debug_getBinderSentTransactions },
1135    { "getBinderReceivedTransactions", "()I",
1136            (void*) android_os_getBinderReceivedTransactions },
1137    { "getBinderLocalObjectCount", "()I",
1138            (void*)android_os_Debug_getLocalObjectCount },
1139    { "getBinderProxyObjectCount", "()I",
1140            (void*)android_os_Debug_getProxyObjectCount },
1141    { "getBinderDeathObjectCount", "()I",
1142            (void*)android_os_Debug_getDeathObjectCount },
1143    { "dumpJavaBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
1144            (void*)android_os_Debug_dumpJavaBacktraceToFileTimeout },
1145    { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)Z",
1146            (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout },
1147    { "getUnreachableMemory", "(IZ)Ljava/lang/String;",
1148            (void*)android_os_Debug_getUnreachableMemory },
1149};
1150
1151int register_android_os_Debug(JNIEnv *env)
1152{
1153    jclass clazz = env->FindClass("android/os/Debug$MemoryInfo");
1154
1155    // Sanity check the number of other statistics expected in Java matches here.
1156    jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I");
1157    jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field);
1158    jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I");
1159    jint numDvkStats = env->GetStaticIntField(clazz, numDvkStats_field);
1160    int expectedNumOtherStats = _NUM_HEAP - _NUM_CORE_HEAP;
1161    if ((numOtherStats + numDvkStats) != expectedNumOtherStats) {
1162        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
1163                             "android.os.Debug.Meminfo.NUM_OTHER_STATS+android.os.Debug.Meminfo.NUM_DVK_STATS=%d expected %d",
1164                             numOtherStats+numDvkStats, expectedNumOtherStats);
1165        return JNI_ERR;
1166    }
1167
1168    otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
1169    hasSwappedOutPss_field = env->GetFieldID(clazz, "hasSwappedOutPss", "Z");
1170
1171    for (int i=0; i<_NUM_CORE_HEAP; i++) {
1172        stat_fields[i].pss_field =
1173                env->GetFieldID(clazz, stat_field_names[i].pss_name, "I");
1174        stat_fields[i].pssSwappable_field =
1175                env->GetFieldID(clazz, stat_field_names[i].pssSwappable_name, "I");
1176        stat_fields[i].privateDirty_field =
1177                env->GetFieldID(clazz, stat_field_names[i].privateDirty_name, "I");
1178        stat_fields[i].sharedDirty_field =
1179                env->GetFieldID(clazz, stat_field_names[i].sharedDirty_name, "I");
1180        stat_fields[i].privateClean_field =
1181                env->GetFieldID(clazz, stat_field_names[i].privateClean_name, "I");
1182        stat_fields[i].sharedClean_field =
1183                env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
1184        stat_fields[i].swappedOut_field =
1185                env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
1186        stat_fields[i].swappedOutPss_field =
1187                env->GetFieldID(clazz, stat_field_names[i].swappedOutPss_name, "I");
1188    }
1189
1190    return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
1191}
1192
1193}; // namespace android
1194