Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2015 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 package com.android.internal.os;
     17 
     18 import android.annotation.Nullable;
     19 import android.os.SystemClock;
     20 import android.text.TextUtils;
     21 import android.util.Slog;
     22 import android.util.SparseLongArray;
     23 import android.util.TimeUtils;
     24 
     25 import java.io.BufferedReader;
     26 import java.io.FileReader;
     27 import java.io.FileWriter;
     28 import java.io.IOException;
     29 
     30 /**
     31  * Reads /proc/uid_cputime/show_uid_stat which has the line format:
     32  *
     33  * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
     34  *
     35  * This provides the time a UID's processes spent executing in user-space and kernel-space.
     36  * The file contains a monotonically increasing count of time for a single boot. This class
     37  * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
     38  * delta.
     39  */
     40 public class KernelUidCpuTimeReader {
     41     private static final String TAG = "KernelUidCpuTimeReader";
     42     private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
     43     private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
     44 
     45     /**
     46      * Callback interface for processing each line of the proc file.
     47      */
     48     public interface Callback {
     49         /**
     50          * @param uid UID of the app
     51          * @param userTimeUs time spent executing in user space in microseconds
     52          * @param systemTimeUs time spent executing in kernel space in microseconds
     53          * @param powerMaUs power consumed executing, in milli-ampere microseconds
     54          */
     55         void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs, long powerMaUs);
     56     }
     57 
     58     private SparseLongArray mLastUserTimeUs = new SparseLongArray();
     59     private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
     60     private SparseLongArray mLastPowerMaUs = new SparseLongArray();
     61     private long mLastTimeReadUs = 0;
     62 
     63     /**
     64      * Reads the proc file, calling into the callback with a delta of time for each UID.
     65      * @param callback The callback to invoke for each line of the proc file. If null,
     66      *                 the data is consumed and subsequent calls to readDelta will provide
     67      *                 a fresh delta.
     68      */
     69     public void readDelta(@Nullable Callback callback) {
     70         long nowUs = SystemClock.elapsedRealtime() * 1000;
     71         try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
     72             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
     73             String line;
     74             while ((line = reader.readLine()) != null) {
     75                 splitter.setString(line);
     76                 final String uidStr = splitter.next();
     77                 final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
     78                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
     79                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
     80                 final long powerMaUs;
     81                 if (splitter.hasNext()) {
     82                     powerMaUs = Long.parseLong(splitter.next(), 10) / 1000;
     83                 } else {
     84                     powerMaUs = 0;
     85                 }
     86 
     87                 // Only report if there is a callback and if this is not the first read.
     88                 if (callback != null && mLastTimeReadUs != 0) {
     89                     long userTimeDeltaUs = userTimeUs;
     90                     long systemTimeDeltaUs = systemTimeUs;
     91                     long powerDeltaMaUs = powerMaUs;
     92                     int index = mLastUserTimeUs.indexOfKey(uid);
     93                     if (index >= 0) {
     94                         userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
     95                         systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
     96                         powerDeltaMaUs -= mLastPowerMaUs.valueAt(index);
     97 
     98                         final long timeDiffUs = nowUs - mLastTimeReadUs;
     99                         if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 || powerDeltaMaUs < 0) {
    100                             StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
    101                             sb.append(uid).append("!\n");
    102                             sb.append("Time between reads: ");
    103                             TimeUtils.formatDuration(timeDiffUs / 1000, sb);
    104                             sb.append("\n");
    105                             sb.append("Previous times: u=");
    106                             TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
    107                             sb.append(" s=");
    108                             TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
    109                             sb.append(" p=").append(mLastPowerMaUs.valueAt(index) / 1000);
    110                             sb.append("mAms\n");
    111 
    112                             sb.append("Current times: u=");
    113                             TimeUtils.formatDuration(userTimeUs / 1000, sb);
    114                             sb.append(" s=");
    115                             TimeUtils.formatDuration(systemTimeUs / 1000, sb);
    116                             sb.append(" p=").append(powerMaUs / 1000);
    117                             sb.append("mAms\n");
    118                             sb.append("Delta: u=");
    119                             TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
    120                             sb.append(" s=");
    121                             TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
    122                             sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
    123                             Slog.e(TAG, sb.toString());
    124 
    125                             userTimeDeltaUs = 0;
    126                             systemTimeDeltaUs = 0;
    127                             powerDeltaMaUs = 0;
    128                         }
    129                     }
    130 
    131                     if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0 || powerDeltaMaUs != 0) {
    132                         callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs,
    133                                 powerDeltaMaUs);
    134                     }
    135                 }
    136                 mLastUserTimeUs.put(uid, userTimeUs);
    137                 mLastSystemTimeUs.put(uid, systemTimeUs);
    138                 mLastPowerMaUs.put(uid, powerMaUs);
    139             }
    140         } catch (IOException e) {
    141             Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
    142         }
    143         mLastTimeReadUs = nowUs;
    144     }
    145 
    146     /**
    147      * Removes the UID from the kernel module and from internal accounting data.
    148      * @param uid The UID to remove.
    149      */
    150     public void removeUid(int uid) {
    151         int index = mLastUserTimeUs.indexOfKey(uid);
    152         if (index >= 0) {
    153             mLastUserTimeUs.removeAt(index);
    154             mLastSystemTimeUs.removeAt(index);
    155             mLastPowerMaUs.removeAt(index);
    156         }
    157 
    158         try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
    159             writer.write(Integer.toString(uid) + "-" + Integer.toString(uid));
    160             writer.flush();
    161         } catch (IOException e) {
    162             Slog.e(TAG, "failed to remove uid from uid_cputime module", e);
    163         }
    164     }
    165 }
    166