From f57e2bceb9f09b0a06ebfe89cd5fd46efcfd6fc8 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 6 Mar 2012 11:27:10 -0800 Subject: 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 --- libs/cpustats/ThreadCpuUsage.cpp | 127 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/cpustats/ThreadCpuUsage.cpp b/libs/cpustats/ThreadCpuUsage.cpp index ffee039..99b4c83 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 +#include #include +#include #include #include +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 -- cgit v1.1