Home | History | Annotate | Download | only in apprtc
      1 /*
      2  *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 package org.appspot.apprtc;
     12 
     13 import android.util.Log;
     14 
     15 import java.io.BufferedReader;
     16 import java.io.FileNotFoundException;
     17 import java.io.FileReader;
     18 import java.io.IOException;
     19 import java.util.InputMismatchException;
     20 import java.util.Scanner;
     21 
     22 /**
     23  * Simple CPU monitor.  The caller creates a CpuMonitor object which can then
     24  * be used via sampleCpuUtilization() to collect the percentual use of the
     25  * cumulative CPU capacity for all CPUs running at their nominal frequency.  3
     26  * values are generated: (1) getCpuCurrent() returns the use since the last
     27  * sampleCpuUtilization(), (2) getCpuAvg3() returns the use since 3 prior
     28  * calls, and (3) getCpuAvgAll() returns the use over all SAMPLE_SAVE_NUMBER
     29  * calls.
     30  *
     31  * <p>CPUs in Android are often "offline", and while this of course means 0 Hz
     32  * as current frequency, in this state we cannot even get their nominal
     33  * frequency.  We therefore tread carefully, and allow any CPU to be missing.
     34  * Missing CPUs are assumed to have the same nominal frequency as any close
     35  * lower-numbered CPU, but as soon as it is online, we'll get their proper
     36  * frequency and remember it.  (Since CPU 0 in practice always seem to be
     37  * online, this unidirectional frequency inheritance should be no problem in
     38  * practice.)
     39  *
     40  * <p>Caveats:
     41  *   o No provision made for zany "turbo" mode, common in the x86 world.
     42  *   o No provision made for ARM big.LITTLE; if CPU n can switch behind our
     43  *     back, we might get incorrect estimates.
     44  *   o This is not thread-safe.  To call asynchronously, create different
     45  *     CpuMonitor objects.
     46  *
     47  * <p>If we can gather enough info to generate a sensible result,
     48  * sampleCpuUtilization returns true.  It is designed to never through an
     49  * exception.
     50  *
     51  * <p>sampleCpuUtilization should not be called too often in its present form,
     52  * since then deltas would be small and the percent values would fluctuate and
     53  * be unreadable. If it is desirable to call it more often than say once per
     54  * second, one would need to increase SAMPLE_SAVE_NUMBER and probably use
     55  * Queue<Integer> to avoid copying overhead.
     56  *
     57  * <p>Known problems:
     58  *   1. Nexus 7 devices running Kitkat have a kernel which often output an
     59  *      incorrect 'idle' field in /proc/stat.  The value is close to twice the
     60  *      correct value, and then returns to back to correct reading.  Both when
     61  *      jumping up and back down we might create faulty CPU load readings.
     62  */
     63 
     64 class CpuMonitor {
     65   private static final int SAMPLE_SAVE_NUMBER = 10;  // Assumed to be >= 3.
     66   private int[] percentVec = new int[SAMPLE_SAVE_NUMBER];
     67   private int sum3 = 0;
     68   private int sum10 = 0;
     69   private static final String TAG = "CpuMonitor";
     70   private long[] cpuFreq;
     71   private int cpusPresent;
     72   private double lastPercentFreq = -1;
     73   private int cpuCurrent;
     74   private int cpuAvg3;
     75   private int cpuAvgAll;
     76   private boolean initialized = false;
     77   private String[] maxPath;
     78   private String[] curPath;
     79   ProcStat lastProcStat;
     80 
     81   private class ProcStat {
     82     final long runTime;
     83     final long idleTime;
     84 
     85     ProcStat(long aRunTime, long aIdleTime) {
     86       runTime = aRunTime;
     87       idleTime = aIdleTime;
     88     }
     89   }
     90 
     91   private void init() {
     92     try {
     93       FileReader fin = new FileReader("/sys/devices/system/cpu/present");
     94       try {
     95         BufferedReader rdr = new BufferedReader(fin);
     96         Scanner scanner = new Scanner(rdr).useDelimiter("[-\n]");
     97         scanner.nextInt();  // Skip leading number 0.
     98         cpusPresent = 1 + scanner.nextInt();
     99         scanner.close();
    100       } catch (Exception e) {
    101         Log.e(TAG, "Cannot do CPU stats due to /sys/devices/system/cpu/present parsing problem");
    102       } finally {
    103         fin.close();
    104       }
    105     } catch (FileNotFoundException e) {
    106       Log.e(TAG, "Cannot do CPU stats since /sys/devices/system/cpu/present is missing");
    107     } catch (IOException e) {
    108       Log.e(TAG, "Error closing file");
    109     }
    110 
    111     cpuFreq = new long [cpusPresent];
    112     maxPath = new String [cpusPresent];
    113     curPath = new String [cpusPresent];
    114     for (int i = 0; i < cpusPresent; i++) {
    115       cpuFreq[i] = 0;  // Frequency "not yet determined".
    116       maxPath[i] = "/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq";
    117       curPath[i] = "/sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq";
    118     }
    119 
    120     lastProcStat = new ProcStat(0, 0);
    121 
    122     initialized = true;
    123   }
    124 
    125   /**
    126    * Re-measure CPU use.  Call this method at an interval of around 1/s.
    127    * This method returns true on success.  The fields
    128    * cpuCurrent, cpuAvg3, and cpuAvgAll are updated on success, and represents:
    129    * cpuCurrent: The CPU use since the last sampleCpuUtilization call.
    130    * cpuAvg3: The average CPU over the last 3 calls.
    131    * cpuAvgAll: The average CPU over the last SAMPLE_SAVE_NUMBER calls.
    132    */
    133   public boolean sampleCpuUtilization() {
    134     long lastSeenMaxFreq = 0;
    135     long cpufreqCurSum = 0;
    136     long cpufreqMaxSum = 0;
    137 
    138     if (!initialized) {
    139       init();
    140     }
    141 
    142     for (int i = 0; i < cpusPresent; i++) {
    143       /*
    144        * For each CPU, attempt to first read its max frequency, then its
    145        * current frequency.  Once as the max frequency for a CPU is found,
    146        * save it in cpuFreq[].
    147        */
    148 
    149       if (cpuFreq[i] == 0) {
    150         // We have never found this CPU's max frequency.  Attempt to read it.
    151         long cpufreqMax = readFreqFromFile(maxPath[i]);
    152         if (cpufreqMax > 0) {
    153           lastSeenMaxFreq = cpufreqMax;
    154           cpuFreq[i] = cpufreqMax;
    155           maxPath[i] = null;  // Kill path to free its memory.
    156         }
    157       } else {
    158         lastSeenMaxFreq = cpuFreq[i];  // A valid, previously read value.
    159       }
    160 
    161       long cpufreqCur = readFreqFromFile(curPath[i]);
    162       cpufreqCurSum += cpufreqCur;
    163 
    164       /* Here, lastSeenMaxFreq might come from
    165        * 1. cpuFreq[i], or
    166        * 2. a previous iteration, or
    167        * 3. a newly read value, or
    168        * 4. hypothetically from the pre-loop dummy.
    169        */
    170       cpufreqMaxSum += lastSeenMaxFreq;
    171     }
    172 
    173     if (cpufreqMaxSum == 0) {
    174       Log.e(TAG, "Could not read max frequency for any CPU");
    175       return false;
    176     }
    177 
    178     /*
    179      * Since the cycle counts are for the period between the last invocation
    180      * and this present one, we average the percentual CPU frequencies between
    181      * now and the beginning of the measurement period.  This is significantly
    182      * incorrect only if the frequencies have peeked or dropped in between the
    183      * invocations.
    184      */
    185     double newPercentFreq = 100.0 * cpufreqCurSum / cpufreqMaxSum;
    186     double percentFreq;
    187     if (lastPercentFreq > 0) {
    188       percentFreq = (lastPercentFreq + newPercentFreq) * 0.5;
    189     } else {
    190       percentFreq = newPercentFreq;
    191     }
    192     lastPercentFreq = newPercentFreq;
    193 
    194     ProcStat procStat = readIdleAndRunTime();
    195     if (procStat == null) {
    196       return false;
    197     }
    198 
    199     long diffRunTime = procStat.runTime - lastProcStat.runTime;
    200     long diffIdleTime = procStat.idleTime - lastProcStat.idleTime;
    201 
    202     // Save new measurements for next round's deltas.
    203     lastProcStat = procStat;
    204 
    205     long allTime = diffRunTime + diffIdleTime;
    206     int percent = allTime == 0 ? 0 : (int) Math.round(percentFreq * diffRunTime / allTime);
    207     percent = Math.max(0, Math.min(percent, 100));
    208 
    209     // Subtract old relevant measurement, add newest.
    210     sum3 += percent - percentVec[2];
    211     // Subtract oldest measurement, add newest.
    212     sum10 += percent - percentVec[SAMPLE_SAVE_NUMBER - 1];
    213 
    214     // Rotate saved percent values, save new measurement in vacated spot.
    215     for (int i = SAMPLE_SAVE_NUMBER - 1; i > 0; i--) {
    216       percentVec[i] = percentVec[i - 1];
    217     }
    218     percentVec[0] = percent;
    219 
    220     cpuCurrent = percent;
    221     cpuAvg3 = sum3 / 3;
    222     cpuAvgAll = sum10 / SAMPLE_SAVE_NUMBER;
    223 
    224     return true;
    225   }
    226 
    227   public int getCpuCurrent() {
    228     return cpuCurrent;
    229   }
    230 
    231   public int getCpuAvg3() {
    232     return cpuAvg3;
    233   }
    234 
    235   public int getCpuAvgAll() {
    236     return cpuAvgAll;
    237   }
    238 
    239   /**
    240    * Read a single integer value from the named file.  Return the read value
    241    * or if an error occurs return 0.
    242    */
    243   private long readFreqFromFile(String fileName) {
    244     long number = 0;
    245     try {
    246       FileReader fin = new FileReader(fileName);
    247       try {
    248         BufferedReader rdr = new BufferedReader(fin);
    249         Scanner scannerC = new Scanner(rdr);
    250         number = scannerC.nextLong();
    251         scannerC.close();
    252       } catch (Exception e) {
    253         // CPU presumably got offline just after we opened file.
    254       } finally {
    255         fin.close();
    256       }
    257     } catch (FileNotFoundException e) {
    258       // CPU is offline, not an error.
    259     } catch (IOException e) {
    260       Log.e(TAG, "Error closing file");
    261     }
    262     return number;
    263   }
    264 
    265   /*
    266    * Read the current utilization of all CPUs using the cumulative first line
    267    * of /proc/stat.
    268    */
    269   private ProcStat readIdleAndRunTime() {
    270     long runTime = 0;
    271     long idleTime = 0;
    272     try {
    273       FileReader fin = new FileReader("/proc/stat");
    274       try {
    275         BufferedReader rdr = new BufferedReader(fin);
    276         Scanner scanner = new Scanner(rdr);
    277         scanner.next();
    278         long user = scanner.nextLong();
    279         long nice = scanner.nextLong();
    280         long sys = scanner.nextLong();
    281         runTime = user + nice + sys;
    282         idleTime = scanner.nextLong();
    283         scanner.close();
    284       } catch (Exception e) {
    285         Log.e(TAG, "Problems parsing /proc/stat");
    286         return null;
    287       } finally {
    288         fin.close();
    289       }
    290     } catch (FileNotFoundException e) {
    291       Log.e(TAG, "Cannot open /proc/stat for reading");
    292       return null;
    293     } catch (IOException e) {
    294       Log.e(TAG, "Problems reading /proc/stat");
    295       return null;
    296     }
    297     return new ProcStat(runTime, idleTime);
    298   }
    299 }
    300