Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1734)

Unified Diff: webrtc/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java

Issue 1813053007: Update CPU Monitor to report CPU frequency and battery level. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java
index 1d54e5e16531acf39d92dddc5a5e69a54b950038..45f0bad2233025a6f718f31b9d11008c6d9c7d01 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java
@@ -10,15 +10,27 @@
package org.appspot.apprtc;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.SystemClock;
import android.util.Log;
import java.io.BufferedReader;
+import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
-import java.util.InputMismatchException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Scanner;
+import org.appspot.apprtc.util.LooperExecutor;
+
/**
* Simple CPU monitor. The caller creates a CpuMonitor object which can then
* be used via sampleCpuUtilization() to collect the percentual use of the
@@ -62,29 +74,142 @@ import java.util.Scanner;
*/
class CpuMonitor {
- private static final int SAMPLE_SAVE_NUMBER = 10; // Assumed to be >= 3.
- private int[] percentVec = new int[SAMPLE_SAVE_NUMBER];
- private int sum3 = 0;
- private int sum10 = 0;
private static final String TAG = "CpuMonitor";
- private long[] cpuFreq;
+ private static final String DUMP_FILE = "log.txt";
wzh 2016/03/21 16:47:06 nit: maybe use stat_log.txt? log.txt is very gener
AlexG 2016/03/21 20:24:49 Done.
+ private static final int CPU_STAT_SAMPLE_PERIOD = 2000;
+ private static final int CPU_STAT_LOG_PERIOD = 6000;
+
+ private final Context appContext;
+ private LooperExecutor executor;
+ private long lastStatLogTimeMs;
+ private int iterations;
+ private double currentUserCpuUsage;
+ private double currentSystemCpuUsage;
+ private double currentTotalCpuUsage;
+ private double currentFrequencyScale = -1;
+ private double sumUserCpuUsage;
+ private double sumSystemCpuUsage;
+ private double sumFrequencyScale;
+ private double sumTotalCpuUsage;
+ private long[] cpuFreqMax;
private int cpusPresent;
- private double lastPercentFreq = -1;
- private int cpuCurrent;
- private int cpuAvg3;
- private int cpuAvgAll;
+ private int actualCpusPresent;
private boolean initialized = false;
private String[] maxPath;
private String[] curPath;
- ProcStat lastProcStat;
+ private double[] curFreqScales;
+ private ProcStat lastProcStat;
+
+ private static boolean dumpEnabled = false;
+ private static FileOutputStream fileWriter;
private class ProcStat {
- final long runTime;
+ final long userTime;
+ final long systemTime;
final long idleTime;
- ProcStat(long aRunTime, long aIdleTime) {
- runTime = aRunTime;
- idleTime = aIdleTime;
+ ProcStat(long userTime, long systemTime, long idleTime) {
+ this.userTime = userTime;
+ this.systemTime = systemTime;
+ this.idleTime = idleTime;
+ }
+ }
+
+ public CpuMonitor(Context context) {
+ Log.d(TAG, "CpuMonitor ctor.");
+ appContext = context.getApplicationContext();
+ lastStatLogTimeMs = 0;
+
+ executor = new LooperExecutor();
+ executor.requestStart();
+ scheduleCpuUtilizationTask();
+ }
+
+ public void release() {
+ if (executor != null) {
+ Log.d(TAG, "release");
+ executor.cancelScheduledTasks();
+ executor.requestStop();
+ executor = null;
+ }
+ }
+
+ public void pause() {
+ if (executor != null) {
+ Log.d(TAG, "pause");
+ executor.cancelScheduledTasks();
+ }
+ }
+
+ public void resume() {
+ if (executor != null) {
+ Log.d(TAG, "resume");
+ resetStat();
+ scheduleCpuUtilizationTask();
+ }
+ }
+
+ public synchronized int getCpuUsageCurrent() {
+ return doubleToPercent(currentTotalCpuUsage);
+ }
+
+ public synchronized int getCpuUsageAverage() {
+ return sumDoubleToPercent(sumTotalCpuUsage, iterations);
+ }
+
+ public synchronized int getCpuFrequencyScaleCurrent() {
+ return doubleToPercent(currentFrequencyScale);
+ }
+
+ private void scheduleCpuUtilizationTask() {
+ executor.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ logCpuUtilization();
+ }
+ }, CPU_STAT_SAMPLE_PERIOD);
+ }
+
+ private void checkDump(String statString) {
+ if (!dumpEnabled) {
+ return;
+ }
+ if (fileWriter == null) {
+ Log.d(TAG, "Start log dump");
+ String fileName = Environment.getExternalStorageDirectory().getAbsolutePath()
+ + File.separator + DUMP_FILE;
+ try {
+ fileWriter = new FileOutputStream(fileName, false /* append */);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Can not open file.", e);
+ dumpEnabled = false;
+ return;
+ }
+ }
+
+ Date date = Calendar.getInstance().getTime();
+ SimpleDateFormat df = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ String msg = df.format(date) + " " + TAG + ":" + statString + "\n";
+ try {
+ fileWriter.write(msg.getBytes());
+ } catch (IOException e) {
+ Log.e(TAG, "Can not write to file.", e);
+ dumpEnabled = false;
+ }
+ }
+
+ private void logCpuUtilization() {
+ boolean logStatistics = false;
+ if (SystemClock.elapsedRealtime() - lastStatLogTimeMs >= CPU_STAT_LOG_PERIOD) {
+ lastStatLogTimeMs = SystemClock.elapsedRealtime();
+ logStatistics = true;
+ }
+ boolean cpuMonitorAvailable = sampleCpuUtilization();
+ if (logStatistics && cpuMonitorAvailable) {
+ String statString = getStatString();
+ checkDump(statString);
+ Log.d(TAG, statString);
+ resetStat();
}
}
@@ -92,8 +217,8 @@ class CpuMonitor {
try {
FileReader fin = new FileReader("/sys/devices/system/cpu/present");
try {
- BufferedReader rdr = new BufferedReader(fin);
- Scanner scanner = new Scanner(rdr).useDelimiter("[-\n]");
+ BufferedReader reader = new BufferedReader(fin);
+ Scanner scanner = new Scanner(reader).useDelimiter("[-\n]");
scanner.nextInt(); // Skip leading number 0.
cpusPresent = 1 + scanner.nextInt();
scanner.close();
@@ -108,20 +233,45 @@ class CpuMonitor {
Log.e(TAG, "Error closing file");
}
- cpuFreq = new long [cpusPresent];
- maxPath = new String [cpusPresent];
- curPath = new String [cpusPresent];
+ cpuFreqMax = new long[cpusPresent];
+ maxPath = new String[cpusPresent];
+ curPath = new String[cpusPresent];
+ curFreqScales = new double[cpusPresent];
for (int i = 0; i < cpusPresent; i++) {
- cpuFreq[i] = 0; // Frequency "not yet determined".
+ cpuFreqMax[i] = 0; // Frequency "not yet determined".
+ curFreqScales[i] = 0;
maxPath[i] = "/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq";
curPath[i] = "/sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq";
}
- lastProcStat = new ProcStat(0, 0);
+ lastProcStat = new ProcStat(0, 0, 0);
+ resetStat();
initialized = true;
}
+ private synchronized void resetStat() {
+ sumUserCpuUsage = 0;
+ sumSystemCpuUsage = 0;
+ sumFrequencyScale = 0;
+ sumTotalCpuUsage = 0;
+ iterations = 0;
+ }
+
+ private int getBatteryLevel() {
+ // Use sticky broadcast with null receiver to read battery level once only.
+ Intent intent = appContext.registerReceiver(
+ null /* receiver */, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+ int batteryLevel = 0;
+ int batteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
+ if (batteryScale > 0) {
+ batteryLevel = (int) (
+ 100f * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0) / batteryScale);
+ }
+ return batteryLevel;
+ }
+
/**
* Re-measure CPU use. Call this method at an interval of around 1/s.
* This method returns true on success. The fields
@@ -130,36 +280,48 @@ class CpuMonitor {
* cpuAvg3: The average CPU over the last 3 calls.
* cpuAvgAll: The average CPU over the last SAMPLE_SAVE_NUMBER calls.
*/
- public boolean sampleCpuUtilization() {
+ private synchronized boolean sampleCpuUtilization() {
long lastSeenMaxFreq = 0;
- long cpufreqCurSum = 0;
- long cpufreqMaxSum = 0;
+ long cpuFreqCurSum = 0;
+ long cpuFreqMaxSum = 0;
if (!initialized) {
init();
}
+ if (cpusPresent == 0) {
+ return false;
+ }
+ actualCpusPresent = 0;
for (int i = 0; i < cpusPresent; i++) {
/*
* For each CPU, attempt to first read its max frequency, then its
* current frequency. Once as the max frequency for a CPU is found,
- * save it in cpuFreq[].
+ * save it in cpuFreqMax[].
*/
- if (cpuFreq[i] == 0) {
+ curFreqScales[i] = 0;
+ if (cpuFreqMax[i] == 0) {
// We have never found this CPU's max frequency. Attempt to read it.
long cpufreqMax = readFreqFromFile(maxPath[i]);
if (cpufreqMax > 0) {
lastSeenMaxFreq = cpufreqMax;
- cpuFreq[i] = cpufreqMax;
+ cpuFreqMax[i] = cpufreqMax;
maxPath[i] = null; // Kill path to free its memory.
}
} else {
- lastSeenMaxFreq = cpuFreq[i]; // A valid, previously read value.
+ lastSeenMaxFreq = cpuFreqMax[i]; // A valid, previously read value.
}
- long cpufreqCur = readFreqFromFile(curPath[i]);
- cpufreqCurSum += cpufreqCur;
+ long cpuFreqCur = readFreqFromFile(curPath[i]);
+ if (cpuFreqCur == 0 && lastSeenMaxFreq == 0) {
+ // No current frequency information for this CPU core - ignore it.
+ continue;
+ }
+ if (cpuFreqCur > 0) {
+ actualCpusPresent++;
+ }
+ cpuFreqCurSum += cpuFreqCur;
/* Here, lastSeenMaxFreq might come from
* 1. cpuFreq[i], or
@@ -167,11 +329,14 @@ class CpuMonitor {
* 3. a newly read value, or
* 4. hypothetically from the pre-loop dummy.
*/
- cpufreqMaxSum += lastSeenMaxFreq;
+ cpuFreqMaxSum += lastSeenMaxFreq;
+ if (lastSeenMaxFreq > 0) {
+ curFreqScales[i] = (double) cpuFreqCur / lastSeenMaxFreq;
+ }
}
- if (cpufreqMaxSum == 0) {
- Log.e(TAG, "Could not read max frequency for any CPU");
+ if (cpuFreqCurSum == 0 || cpuFreqMaxSum == 0) {
+ Log.e(TAG, "Could not read max or current frequency for any CPU");
return false;
}
@@ -182,58 +347,85 @@ class CpuMonitor {
* incorrect only if the frequencies have peeked or dropped in between the
* invocations.
*/
- double newPercentFreq = 100.0 * cpufreqCurSum / cpufreqMaxSum;
- double percentFreq;
- if (lastPercentFreq > 0) {
- percentFreq = (lastPercentFreq + newPercentFreq) * 0.5;
+ double newFrequencyScale = (double) cpuFreqCurSum / cpuFreqMaxSum;
+ double frequencyScale;
+ if (currentFrequencyScale > 0) {
+ frequencyScale = (currentFrequencyScale + newFrequencyScale) * 0.5;
} else {
- percentFreq = newPercentFreq;
+ frequencyScale = newFrequencyScale;
}
- lastPercentFreq = newPercentFreq;
- ProcStat procStat = readIdleAndRunTime();
+ ProcStat procStat = readProcStat();
if (procStat == null) {
return false;
}
- long diffRunTime = procStat.runTime - lastProcStat.runTime;
+ long diffUserTime = procStat.userTime - lastProcStat.userTime;
+ long diffSystemTime = procStat.systemTime - lastProcStat.systemTime;
long diffIdleTime = procStat.idleTime - lastProcStat.idleTime;
+ long allTime = diffUserTime + diffSystemTime + diffIdleTime;
- // Save new measurements for next round's deltas.
- lastProcStat = procStat;
+ if (frequencyScale == 0 || allTime == 0) {
+ return false;
+ }
- long allTime = diffRunTime + diffIdleTime;
- int percent = allTime == 0 ? 0 : (int) Math.round(percentFreq * diffRunTime / allTime);
- percent = Math.max(0, Math.min(percent, 100));
+ // Update statistics.
+ currentFrequencyScale = frequencyScale;
+ sumFrequencyScale += frequencyScale;
- // Subtract old relevant measurement, add newest.
- sum3 += percent - percentVec[2];
- // Subtract oldest measurement, add newest.
- sum10 += percent - percentVec[SAMPLE_SAVE_NUMBER - 1];
+ currentUserCpuUsage = (double) diffUserTime / allTime;
+ sumUserCpuUsage += currentUserCpuUsage;
- // Rotate saved percent values, save new measurement in vacated spot.
- for (int i = SAMPLE_SAVE_NUMBER - 1; i > 0; i--) {
- percentVec[i] = percentVec[i - 1];
- }
- percentVec[0] = percent;
+ currentSystemCpuUsage = (double) diffSystemTime / allTime;
+ sumSystemCpuUsage += currentSystemCpuUsage;
- cpuCurrent = percent;
- cpuAvg3 = sum3 / 3;
- cpuAvgAll = sum10 / SAMPLE_SAVE_NUMBER;
+ currentTotalCpuUsage = (currentUserCpuUsage + currentSystemCpuUsage) * currentFrequencyScale;
+ sumTotalCpuUsage += currentTotalCpuUsage;
+
+ iterations++;
+ // Save new measurements for next round's deltas.
+ lastProcStat = procStat;
return true;
}
- public int getCpuCurrent() {
- return cpuCurrent;
+ private int doubleToPercent(double d) {
+ return (int) (d * 100 + 0.5);
}
- public int getCpuAvg3() {
- return cpuAvg3;
+ private int sumDoubleToPercent(double d, int iterations) {
+ if (iterations > 0) {
+ return (int) (d * 100.0 / (double) iterations + 0.5);
+ } else {
+ return 0;
+ }
}
- public int getCpuAvgAll() {
- return cpuAvgAll;
+ private String getStatString() {
+ StringBuilder stat = new StringBuilder();
+ stat.append("CPU User: ")
+ .append(doubleToPercent(currentUserCpuUsage)).append("/")
+ .append(sumDoubleToPercent(sumUserCpuUsage, iterations)).append(" (")
+ .append(doubleToPercent(currentUserCpuUsage * currentFrequencyScale)).append(")")
+ .append(". System: ")
+ .append(doubleToPercent(currentSystemCpuUsage)).append("/")
+ .append(sumDoubleToPercent(sumSystemCpuUsage, iterations)).append(" (")
+ .append(doubleToPercent(currentSystemCpuUsage * currentFrequencyScale)).append(")")
+ .append(". CPU freq %: ")
+ .append(doubleToPercent(currentFrequencyScale)).append("/")
+ .append(sumDoubleToPercent(sumFrequencyScale, iterations))
+ .append(". Total CPU usage: ")
+ .append(doubleToPercent(currentTotalCpuUsage)).append("/")
+ .append(sumDoubleToPercent(sumTotalCpuUsage, iterations))
+ .append(". Cores: ")
+ .append(actualCpusPresent);
+ stat.append("( ");
+ for (int i = 0; i < cpusPresent; i++) {
+ stat.append(doubleToPercent(curFreqScales[i])).append(" ");
+ }
+ stat.append("). Battery %: ")
+ .append(getBatteryLevel());
+ return stat.toString();
}
/**
@@ -266,8 +458,9 @@ class CpuMonitor {
* Read the current utilization of all CPUs using the cumulative first line
* of /proc/stat.
*/
- private ProcStat readIdleAndRunTime() {
- long runTime = 0;
+ private ProcStat readProcStat() {
+ long userTime = 0;
+ long systemTime = 0;
long idleTime = 0;
try {
FileReader fin = new FileReader("/proc/stat");
@@ -275,11 +468,13 @@ class CpuMonitor {
BufferedReader rdr = new BufferedReader(fin);
Scanner scanner = new Scanner(rdr);
scanner.next();
- long user = scanner.nextLong();
+ userTime = scanner.nextLong();
long nice = scanner.nextLong();
- long sys = scanner.nextLong();
- runTime = user + nice + sys;
+ userTime += nice;
+ systemTime = scanner.nextLong();
idleTime = scanner.nextLong();
+ long ioWaitTime = scanner.nextLong();
+ userTime += ioWaitTime;
scanner.close();
} catch (Exception e) {
Log.e(TAG, "Problems parsing /proc/stat");
@@ -294,6 +489,6 @@ class CpuMonitor {
Log.e(TAG, "Problems reading /proc/stat");
return null;
}
- return new ProcStat(runTime, idleTime);
+ return new ProcStat(userTime, systemTime, idleTime);
}
}

Powered by Google App Engine
This is Rietveld 408576698