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