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