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.StrictMode;
     20 import android.os.SystemClock;
     21 import android.text.TextUtils;
     22 import android.util.Slog;
     23 import android.util.SparseLongArray;
     24 import android.util.TimeUtils;
     25 
     26 import java.io.BufferedReader;
     27 import java.io.FileReader;
     28 import java.io.FileWriter;
     29 import java.io.IOException;
     30 
     31 /**
     32  * Reads /proc/uid_cputime/show_uid_stat which has the line format:
     33  *
     34  * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
     35  *
     36  * This provides the time a UID's processes spent executing in user-space and kernel-space.
     37  * The file contains a monotonically increasing count of time for a single boot. This class
     38  * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
     39  * delta.
     40  */
     41 public class KernelUidCpuTimeReader extends
     42         KernelUidCpuTimeReaderBase<KernelUidCpuTimeReader.Callback> {
     43     private static final String TAG = KernelUidCpuTimeReader.class.getSimpleName();
     44     private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
     45     private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
     46 
     47     /**
     48      * Callback interface for processing each line of the proc file.
     49      */
     50     public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
     51         /**
     52          * @param uid          UID of the app
     53          * @param userTimeUs   time spent executing in user space in microseconds
     54          * @param systemTimeUs time spent executing in kernel space in microseconds
     55          */
     56         void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
     57     }
     58 
     59     private SparseLongArray mLastUserTimeUs = new SparseLongArray();
     60     private SparseLongArray mLastSystemTimeUs = 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      *
     66      * @param callback The callback to invoke for each line of the proc file. If null,
     67      *                 the data is consumed and subsequent calls to readDelta will provide
     68      *                 a fresh delta.
     69      */
     70     @Override
     71     protected void readDeltaImpl(@Nullable Callback callback) {
     72         final int oldMask = StrictMode.allowThreadDiskReadsMask();
     73         long nowUs = SystemClock.elapsedRealtime() * 1000;
     74         try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
     75             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
     76             String line;
     77             while ((line = reader.readLine()) != null) {
     78                 splitter.setString(line);
     79                 final String uidStr = splitter.next();
     80                 final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
     81                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
     82                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
     83 
     84                 boolean notifyCallback = false;
     85                 long userTimeDeltaUs = userTimeUs;
     86                 long systemTimeDeltaUs = systemTimeUs;
     87                 // Only report if there is a callback and if this is not the first read.
     88                 if (callback != null && mLastTimeReadUs != 0) {
     89                     int index = mLastUserTimeUs.indexOfKey(uid);
     90                     if (index >= 0) {
     91                         userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
     92                         systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
     93 
     94                         final long timeDiffUs = nowUs - mLastTimeReadUs;
     95                         if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) {
     96                             StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
     97                             sb.append(uid).append("!\n");
     98                             sb.append("Time between reads: ");
     99                             TimeUtils.formatDuration(timeDiffUs / 1000, sb);
    100                             sb.append("\n");
    101                             sb.append("Previous times: u=");
    102                             TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
    103                             sb.append(" s=");
    104                             TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
    105 
    106                             sb.append("\nCurrent times: u=");
    107                             TimeUtils.formatDuration(userTimeUs / 1000, sb);
    108                             sb.append(" s=");
    109                             TimeUtils.formatDuration(systemTimeUs / 1000, sb);
    110                             sb.append("\nDelta: u=");
    111                             TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
    112                             sb.append(" s=");
    113                             TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
    114                             Slog.e(TAG, sb.toString());
    115 
    116                             userTimeDeltaUs = 0;
    117                             systemTimeDeltaUs = 0;
    118                         }
    119                     }
    120 
    121                     notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
    122                 }
    123                 mLastUserTimeUs.put(uid, userTimeUs);
    124                 mLastSystemTimeUs.put(uid, systemTimeUs);
    125                 if (notifyCallback) {
    126                     callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
    127                 }
    128             }
    129         } catch (IOException e) {
    130             Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
    131         } finally {
    132             StrictMode.setThreadPolicyMask(oldMask);
    133         }
    134         mLastTimeReadUs = nowUs;
    135     }
    136 
    137     /**
    138      * Reads the proc file, calling into the callback with raw absolute value of time for each UID.
    139      * @param callback The callback to invoke for each line of the proc file.
    140      */
    141     public void readAbsolute(Callback callback) {
    142         final int oldMask = StrictMode.allowThreadDiskReadsMask();
    143         try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
    144             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
    145             String line;
    146             while ((line = reader.readLine()) != null) {
    147                 splitter.setString(line);
    148                 final String uidStr = splitter.next();
    149                 final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
    150                 final long userTimeUs = Long.parseLong(splitter.next(), 10);
    151                 final long systemTimeUs = Long.parseLong(splitter.next(), 10);
    152                 callback.onUidCpuTime(uid, userTimeUs, systemTimeUs);
    153             }
    154         } catch (IOException e) {
    155             Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
    156         } finally {
    157             StrictMode.setThreadPolicyMask(oldMask);
    158         }
    159     }
    160 
    161     /**
    162      * Removes the UID from the kernel module and from internal accounting data. Only
    163      * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
    164      * visible system wide.
    165      *
    166      * @param uid The UID to remove.
    167      */
    168     public void removeUid(int uid) {
    169         final int index = mLastSystemTimeUs.indexOfKey(uid);
    170         if (index >= 0) {
    171             mLastSystemTimeUs.removeAt(index);
    172             mLastUserTimeUs.removeAt(index);
    173         }
    174         removeUidsFromKernelModule(uid, uid);
    175     }
    176 
    177     /**
    178      * Removes UIDs in a given range from the kernel module and internal accounting data. Only
    179      * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
    180      * visible system wide.
    181      *
    182      * @param startUid the first uid to remove
    183      * @param endUid   the last uid to remove
    184      */
    185     public void removeUidsInRange(int startUid, int endUid) {
    186         if (endUid < startUid) {
    187             return;
    188         }
    189         mLastSystemTimeUs.put(startUid, 0);
    190         mLastUserTimeUs.put(startUid, 0);
    191         mLastSystemTimeUs.put(endUid, 0);
    192         mLastUserTimeUs.put(endUid, 0);
    193         final int startIndex = mLastSystemTimeUs.indexOfKey(startUid);
    194         final int endIndex = mLastSystemTimeUs.indexOfKey(endUid);
    195         mLastSystemTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
    196         mLastUserTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
    197         removeUidsFromKernelModule(startUid, endUid);
    198     }
    199 
    200     private void removeUidsFromKernelModule(int startUid, int endUid) {
    201         Slog.d(TAG, "Removing uids " + startUid + "-" + endUid);
    202         final int oldMask = StrictMode.allowThreadDiskWritesMask();
    203         try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
    204             writer.write(startUid + "-" + endUid);
    205             writer.flush();
    206         } catch (IOException e) {
    207             Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid
    208                     + " from uid_cputime module", e);
    209         } finally {
    210             StrictMode.setThreadPolicyMask(oldMask);
    211         }
    212     }
    213 }
    214