summaryrefslogtreecommitdiff
path: root/libs/cpustats/ThreadCpuUsage.cpp
diff options
context:
space:
mode:
author Glenn Kasten <gkasten@google.com> 2012-03-06 11:27:10 -0800
committer Glenn Kasten <gkasten@google.com> 2012-03-14 16:03:01 -0700
commitf57e2bceb9f09b0a06ebfe89cd5fd46efcfd6fc8 (patch)
tree14f817b15ff2ee61648f15c54a7d8f6db465ae37 /libs/cpustats/ThreadCpuUsage.cpp
parenta2d68c93941d71194995efdfedc440110d7c5532 (diff)
AudioFlinger playback thread CPU measurement in Hz
Log statistics on CPU usage in Hz in addition to wall clock time Use CPU statistics for all playback threads, not just MIXER (but they are disabled by default by a compile-time debug macro). ThreadCpuUsage library: - Move statistics out of the library and leave that up to the caller - Add API to determine a CPU's frequency Change-Id: Ia1011123146e641fcf210ef26e78ae2b4d3b64ad
Diffstat (limited to 'libs/cpustats/ThreadCpuUsage.cpp')
-rw-r--r--libs/cpustats/ThreadCpuUsage.cpp127
1 files changed, 120 insertions, 7 deletions
diff --git a/libs/cpustats/ThreadCpuUsage.cpp b/libs/cpustats/ThreadCpuUsage.cpp
index ffee039e1ee1..99b4c836394c 100644
--- a/libs/cpustats/ThreadCpuUsage.cpp
+++ b/libs/cpustats/ThreadCpuUsage.cpp
@@ -14,18 +14,26 @@
* limitations under the License.
*/
+#define LOG_TAG "ThreadCpuUsage"
+//#define LOG_NDEBUG 0
+
#include <errno.h>
+#include <stdlib.h>
#include <time.h>
+#include <utils/Debug.h>
#include <utils/Log.h>
#include <cpustats/ThreadCpuUsage.h>
+namespace android {
+
bool ThreadCpuUsage::setEnabled(bool isEnabled)
{
bool wasEnabled = mIsEnabled;
// only do something if there is a change
if (isEnabled != wasEnabled) {
+ ALOGV("setEnabled(%d)", isEnabled);
int rc;
// enabling
if (isEnabled) {
@@ -65,20 +73,28 @@ bool ThreadCpuUsage::setEnabled(bool isEnabled)
return wasEnabled;
}
-void ThreadCpuUsage::sampleAndEnable()
+bool ThreadCpuUsage::sampleAndEnable(double& ns)
{
+ bool ret;
bool wasEverEnabled = mWasEverEnabled;
if (enable()) {
// already enabled, so add a new sample relative to previous
- sample();
+ return sample(ns);
} else if (wasEverEnabled) {
// was disabled, but add sample for accumulated time while enabled
- mStatistics.sample((double) mAccumulator);
+ ns = (double) mAccumulator;
mAccumulator = 0;
+ ALOGV("sampleAndEnable %.0f", ns);
+ return true;
+ } else {
+ // first time called
+ ns = 0.0;
+ ALOGV("sampleAndEnable false");
+ return false;
}
}
-void ThreadCpuUsage::sample()
+bool ThreadCpuUsage::sample(double &ns)
{
if (mWasEverEnabled) {
if (mIsEnabled) {
@@ -87,6 +103,8 @@ void ThreadCpuUsage::sample()
rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
if (rc) {
ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
+ ns = 0.0;
+ return false;
} else {
long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
(ts.tv_nsec - mPreviousTs.tv_nsec);
@@ -96,10 +114,14 @@ void ThreadCpuUsage::sample()
} else {
mWasEverEnabled = false;
}
- mStatistics.sample((double) mAccumulator);
+ ns = (double) mAccumulator;
+ ALOGV("sample %.0f", ns);
mAccumulator = 0;
+ return true;
} else {
ALOGW("Can't add sample because measurements have never been enabled");
+ ns = 0.0;
+ return false;
}
}
@@ -122,12 +144,13 @@ long long ThreadCpuUsage::elapsed() const
ALOGW("Can't compute elapsed time because measurements have never been enabled");
elapsed = 0;
}
+ ALOGV("elapsed %lld", elapsed);
return elapsed;
}
-void ThreadCpuUsage::resetStatistics()
+void ThreadCpuUsage::resetElapsed()
{
- mStatistics.reset();
+ ALOGV("resetElapsed");
if (mMonotonicKnown) {
int rc;
rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
@@ -137,3 +160,93 @@ void ThreadCpuUsage::resetStatistics()
}
}
}
+
+/*static*/
+int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU];
+pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT;
+int ThreadCpuUsage::sKernelMax;
+
+/*static*/
+void ThreadCpuUsage::init()
+{
+ // read the number of CPUs
+ sKernelMax = 1;
+ int fd = open("/sys/devices/system/cpu/kernel_max", O_RDONLY);
+ if (fd >= 0) {
+#define KERNEL_MAX_SIZE 12
+ char kernelMax[KERNEL_MAX_SIZE];
+ ssize_t actual = read(fd, kernelMax, sizeof(kernelMax));
+ if (actual >= 2 && kernelMax[actual-1] == '\n') {
+ sKernelMax = atoi(kernelMax);
+ if (sKernelMax >= MAX_CPU - 1) {
+ ALOGW("kernel_max %d but MAX_CPU %d", sKernelMax, MAX_CPU);
+ sKernelMax = MAX_CPU;
+ } else if (sKernelMax < 0) {
+ ALOGW("kernel_max invalid %d", sKernelMax);
+ sKernelMax = 1;
+ } else {
+ ++sKernelMax;
+ ALOGV("number of CPUs %d", sKernelMax);
+ }
+ } else {
+ ALOGW("Can't read number of CPUs");
+ }
+ (void) close(fd);
+ } else {
+ ALOGW("Can't open number of CPUs");
+ }
+
+ // open fd to each frequency per CPU
+#define FREQ_SIZE 64
+ char freq_path[FREQ_SIZE];
+#define FREQ_DIGIT 27
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
+ strlcpy(freq_path, "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq", sizeof(freq_path));
+ int i;
+ for (i = 0; i < MAX_CPU; ++i) {
+ sScalingFds[i] = -1;
+ }
+ for (i = 0; i < sKernelMax; ++i) {
+ freq_path[FREQ_DIGIT] = i + '0';
+ fd = open(freq_path, O_RDONLY);
+ if (fd >= 0) {
+ // keep this fd until process exit
+ sScalingFds[i] = fd;
+ } else {
+ ALOGW("Can't open CPU %d", i);
+ }
+ }
+}
+
+uint32_t ThreadCpuUsage::getCpukHz(int cpuNum)
+{
+ if (cpuNum < 0 || cpuNum >= MAX_CPU) {
+ ALOGW("getCpukHz called with invalid CPU %d", cpuNum);
+ return 0;
+ }
+ int fd = sScalingFds[cpuNum];
+ if (fd < 0) {
+ ALOGW("getCpukHz called for unopened CPU %d", cpuNum);
+ return 0;
+ }
+#define KHZ_SIZE 12
+ char kHz[KHZ_SIZE]; // kHz base 10
+ ssize_t actual = pread(fd, kHz, sizeof(kHz), (off_t) 0);
+ uint32_t ret;
+ if (actual >= 2 && kHz[actual-1] == '\n') {
+ ret = atoi(kHz);
+ } else {
+ ret = 0;
+ }
+ if (ret != mCurrentkHz[cpuNum]) {
+ if (ret > 0) {
+ ALOGV("CPU %d frequency %u kHz", cpuNum, ret);
+ } else {
+ ALOGW("Can't read CPU %d frequency", cpuNum);
+ }
+ mCurrentkHz[cpuNum] = ret;
+ }
+ return ret;
+}
+
+} // namespace android