Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2018 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 package com.android.server.am;
     18 
     19 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
     20 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
     21 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
     22 
     23 import android.annotation.Nullable;
     24 import android.os.FileUtils;
     25 import android.util.Slog;
     26 
     27 import com.android.internal.annotations.VisibleForTesting;
     28 
     29 import java.io.File;
     30 import java.io.IOException;
     31 import java.util.Locale;
     32 import java.util.regex.Matcher;
     33 import java.util.regex.Pattern;
     34 
     35 /**
     36  * Static utility methods related to {@link MemoryStat}.
     37  */
     38 final class MemoryStatUtil {
     39     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
     40 
     41     /** Path to check if device has memcg */
     42     private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
     43     /** Path to memory stat file for logging app start memory state */
     44     private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
     45     /** Path to procfs stat file for logging app start memory state */
     46     private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
     47 
     48     private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
     49     private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
     50     private static final Pattern RSS_IN_BYTES = Pattern.compile("total_rss (\\d+)");
     51     private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
     52     private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
     53 
     54     private static final int PGFAULT_INDEX = 9;
     55     private static final int PGMAJFAULT_INDEX = 11;
     56     private static final int RSS_IN_BYTES_INDEX = 23;
     57 
     58     /** True if device has memcg */
     59     private static volatile Boolean sDeviceHasMemCg;
     60 
     61     private MemoryStatUtil() {}
     62 
     63     /**
     64      * Reads memory stat for a process.
     65      *
     66      * Reads from memcg if available on device, else fallback to procfs.
     67      * Returns null if no stats can be read.
     68      */
     69     @Nullable
     70     static MemoryStat readMemoryStatFromFilesystem(int uid, int pid) {
     71         return hasMemcg() ? readMemoryStatFromMemcg(uid, pid) : readMemoryStatFromProcfs(pid);
     72     }
     73 
     74     /**
     75      * Reads memory.stat of a process from memcg.
     76      *
     77      * Returns null if file is not found in memcg or if file has unrecognized contents.
     78      */
     79     @Nullable
     80     static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
     81         final String path = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
     82         return parseMemoryStatFromMemcg(readFileContents(path));
     83     }
     84 
     85     /**
     86      * Reads memory stat of a process from procfs.
     87      *
     88      * Returns null if file is not found in procfs or if file has unrecognized contents.
     89      */
     90     @Nullable
     91     static MemoryStat readMemoryStatFromProcfs(int pid) {
     92         final String path = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
     93         return parseMemoryStatFromProcfs(readFileContents(path));
     94     }
     95 
     96     private static String readFileContents(String path) {
     97         final File file = new File(path);
     98         if (!file.exists()) {
     99             if (DEBUG_METRICS) Slog.i(TAG, path + " not found");
    100             return null;
    101         }
    102 
    103         try {
    104             return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
    105         } catch (IOException e) {
    106             Slog.e(TAG, "Failed to read file:", e);
    107             return null;
    108         }
    109     }
    110 
    111     /**
    112      * Parses relevant statistics out from the contents of a memory.stat file in memcg.
    113      */
    114     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    115     @Nullable
    116     static MemoryStat parseMemoryStatFromMemcg(String memoryStatContents) {
    117         if (memoryStatContents == null || memoryStatContents.isEmpty()) {
    118             return null;
    119         }
    120 
    121         final MemoryStat memoryStat = new MemoryStat();
    122         Matcher m;
    123         m = PGFAULT.matcher(memoryStatContents);
    124         memoryStat.pgfault = m.find() ? Long.valueOf(m.group(1)) : 0;
    125         m = PGMAJFAULT.matcher(memoryStatContents);
    126         memoryStat.pgmajfault = m.find() ? Long.valueOf(m.group(1)) : 0;
    127         m = RSS_IN_BYTES.matcher(memoryStatContents);
    128         memoryStat.rssInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
    129         m = CACHE_IN_BYTES.matcher(memoryStatContents);
    130         memoryStat.cacheInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
    131         m = SWAP_IN_BYTES.matcher(memoryStatContents);
    132         memoryStat.swapInBytes = m.find() ? Long.valueOf(m.group(1)) : 0;
    133         return memoryStat;
    134     }
    135 
    136     /**
    137      * Parses relevant statistics out from the contents of a /proc/pid/stat file in procfs.
    138      */
    139     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    140     @Nullable
    141     static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
    142         if (procStatContents == null || procStatContents.isEmpty()) {
    143             return null;
    144         }
    145 
    146         final String[] splits = procStatContents.split(" ");
    147         if (splits.length < 24) {
    148             return null;
    149         }
    150 
    151         final MemoryStat memoryStat = new MemoryStat();
    152         memoryStat.pgfault = Long.valueOf(splits[PGFAULT_INDEX]);
    153         memoryStat.pgmajfault = Long.valueOf(splits[PGMAJFAULT_INDEX]);
    154         memoryStat.rssInBytes = Long.valueOf(splits[RSS_IN_BYTES_INDEX]);
    155         return memoryStat;
    156     }
    157 
    158     /**
    159      * Checks if memcg is available on device.
    160      *
    161      * Touches the filesystem to do the check.
    162      */
    163     static boolean hasMemcg() {
    164         if (sDeviceHasMemCg == null) {
    165             sDeviceHasMemCg = (new File(MEMCG_TEST_PATH)).exists();
    166         }
    167         return sDeviceHasMemCg;
    168     }
    169 
    170     static final class MemoryStat {
    171         /** Number of page faults */
    172         long pgfault;
    173         /** Number of major page faults */
    174         long pgmajfault;
    175         /** Number of bytes of anonymous and swap cache memory */
    176         long rssInBytes;
    177         /** Number of bytes of page cache memory */
    178         long cacheInBytes;
    179         /** Number of bytes of swap usage */
    180         long swapInBytes;
    181     }
    182 }
    183