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 |