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