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.os.Process;
     19 import android.util.Slog;
     20 
     21 import java.io.FileInputStream;
     22 import java.util.Iterator;
     23 
     24 /**
     25  * Reads and parses wakelock stats from the kernel (/proc/wakelocks).
     26  */
     27 public class KernelWakelockReader {
     28     private static final String TAG = "KernelWakelockReader";
     29     private static int sKernelWakelockUpdateVersion = 0;
     30     private static final String sWakelockFile = "/proc/wakelocks";
     31     private static final String sWakeupSourceFile = "/d/wakeup_sources";
     32 
     33     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
     34         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
     35                               Process.PROC_QUOTES,
     36         Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 1: count
     37         Process.PROC_TAB_TERM,
     38         Process.PROC_TAB_TERM,
     39         Process.PROC_TAB_TERM,
     40         Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 5: totalTime
     41     };
     42 
     43     private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
     44         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING,                // 0: name
     45         Process.PROC_TAB_TERM|Process.PROC_COMBINE|
     46                               Process.PROC_OUT_LONG,                  // 1: count
     47         Process.PROC_TAB_TERM|Process.PROC_COMBINE,
     48         Process.PROC_TAB_TERM|Process.PROC_COMBINE,
     49         Process.PROC_TAB_TERM|Process.PROC_COMBINE,
     50         Process.PROC_TAB_TERM|Process.PROC_COMBINE,
     51         Process.PROC_TAB_TERM|Process.PROC_COMBINE
     52                              |Process.PROC_OUT_LONG,                  // 6: totalTime
     53     };
     54 
     55     private final String[] mProcWakelocksName = new String[3];
     56     private final long[] mProcWakelocksData = new long[3];
     57 
     58     /**
     59      * Reads kernel wakelock stats and updates the staleStats with the new information.
     60      * @param staleStats Existing object to update.
     61      * @return the updated data.
     62      */
     63     public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
     64         byte[] buffer = new byte[32*1024];
     65         int len;
     66         boolean wakeup_sources;
     67 
     68         try {
     69             FileInputStream is;
     70             try {
     71                 is = new FileInputStream(sWakelockFile);
     72                 wakeup_sources = false;
     73             } catch (java.io.FileNotFoundException e) {
     74                 try {
     75                     is = new FileInputStream(sWakeupSourceFile);
     76                     wakeup_sources = true;
     77                 } catch (java.io.FileNotFoundException e2) {
     78                     Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
     79                             sWakeupSourceFile + " exists");
     80                     return null;
     81                 }
     82             }
     83 
     84             len = is.read(buffer);
     85             is.close();
     86         } catch (java.io.IOException e) {
     87             Slog.wtf(TAG, "failed to read kernel wakelocks", e);
     88             return null;
     89         }
     90 
     91         if (len > 0) {
     92             if (len >= buffer.length) {
     93                 Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
     94             }
     95             int i;
     96             for (i=0; i<len; i++) {
     97                 if (buffer[i] == '\0') {
     98                     len = i;
     99                     break;
    100                 }
    101             }
    102         }
    103         return parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
    104     }
    105 
    106     /**
    107      * Reads the wakelocks and updates the staleStats with the new information.
    108      */
    109     private KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources,
    110                                                    final KernelWakelockStats staleStats) {
    111         String name;
    112         int count;
    113         long totalTime;
    114         int startIndex;
    115         int endIndex;
    116         int numUpdatedWlNames = 0;
    117 
    118         // Advance past the first line.
    119         int i;
    120         for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
    121         startIndex = endIndex = i + 1;
    122 
    123         synchronized(this) {
    124             sKernelWakelockUpdateVersion++;
    125             while (endIndex < len) {
    126                 for (endIndex=startIndex;
    127                         endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
    128                         endIndex++);
    129                 endIndex++; // endIndex is an exclusive upper bound.
    130                 // Don't go over the end of the buffer, Process.parseProcLine might
    131                 // write to wlBuffer[endIndex]
    132                 if (endIndex >= (len - 1) ) {
    133                     return staleStats;
    134                 }
    135 
    136                 String[] nameStringArray = mProcWakelocksName;
    137                 long[] wlData = mProcWakelocksData;
    138                 // Stomp out any bad characters since this is from a circular buffer
    139                 // A corruption is seen sometimes that results in the vm crashing
    140                 // This should prevent crashes and the line will probably fail to parse
    141                 for (int j = startIndex; j < endIndex; j++) {
    142                     if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
    143                 }
    144                 boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
    145                         wakeup_sources ? WAKEUP_SOURCES_FORMAT :
    146                                          PROC_WAKELOCKS_FORMAT,
    147                         nameStringArray, wlData, null);
    148 
    149                 name = nameStringArray[0];
    150                 count = (int) wlData[1];
    151 
    152                 if (wakeup_sources) {
    153                         // convert milliseconds to microseconds
    154                         totalTime = wlData[2] * 1000;
    155                 } else {
    156                         // convert nanoseconds to microseconds with rounding.
    157                         totalTime = (wlData[2] + 500) / 1000;
    158                 }
    159 
    160                 if (parsed && name.length() > 0) {
    161                     if (!staleStats.containsKey(name)) {
    162                         staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime,
    163                                 sKernelWakelockUpdateVersion));
    164                         numUpdatedWlNames++;
    165                     } else {
    166                         KernelWakelockStats.Entry kwlStats = staleStats.get(name);
    167                         if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
    168                             kwlStats.mCount += count;
    169                             kwlStats.mTotalTime += totalTime;
    170                         } else {
    171                             kwlStats.mCount = count;
    172                             kwlStats.mTotalTime = totalTime;
    173                             kwlStats.mVersion = sKernelWakelockUpdateVersion;
    174                             numUpdatedWlNames++;
    175                         }
    176                     }
    177                 } else if (!parsed) {
    178                     try {
    179                         Slog.wtf(TAG, "Failed to parse proc line: " +
    180                                 new String(wlBuffer, startIndex, endIndex - startIndex));
    181                     } catch (Exception e) {
    182                         Slog.wtf(TAG, "Failed to parse proc line!");
    183                     }
    184                 }
    185                 startIndex = endIndex;
    186             }
    187 
    188             if (staleStats.size() != numUpdatedWlNames) {
    189                 // Don't report old data.
    190                 Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
    191                 while (itr.hasNext()) {
    192                     if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
    193                         itr.remove();
    194                     }
    195                 }
    196             }
    197 
    198             staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
    199             return staleStats;
    200         }
    201     }
    202 }
    203