summaryrefslogtreecommitdiffstats
path: root/media/libcpustats
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcpustats')
-rw-r--r--media/libcpustats/Android.mk11
-rw-r--r--media/libcpustats/CentralTendencyStatistics.cpp81
-rw-r--r--media/libcpustats/ThreadCpuUsage.cpp255
3 files changed, 347 insertions, 0 deletions
diff --git a/media/libcpustats/Android.mk b/media/libcpustats/Android.mk
new file mode 100644
index 0000000..b506353
--- /dev/null
+++ b/media/libcpustats/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ CentralTendencyStatistics.cpp \
+ ThreadCpuUsage.cpp
+
+LOCAL_MODULE := libcpustats
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libcpustats/CentralTendencyStatistics.cpp b/media/libcpustats/CentralTendencyStatistics.cpp
new file mode 100644
index 0000000..42ab62b
--- /dev/null
+++ b/media/libcpustats/CentralTendencyStatistics.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <cpustats/CentralTendencyStatistics.h>
+
+void CentralTendencyStatistics::sample(double x)
+{
+ // update min and max
+ if (x < mMinimum)
+ mMinimum = x;
+ if (x > mMaximum)
+ mMaximum = x;
+ // Knuth
+ if (mN == 0) {
+ mMean = 0;
+ }
+ ++mN;
+ double delta = x - mMean;
+ mMean += delta / mN;
+ mM2 += delta * (x - mMean);
+}
+
+void CentralTendencyStatistics::reset()
+{
+ mMean = NAN;
+ mMedian = NAN;
+ mMinimum = INFINITY;
+ mMaximum = -INFINITY;
+ mN = 0;
+ mM2 = 0;
+ mVariance = NAN;
+ mVarianceKnownForN = 0;
+ mStddev = NAN;
+ mStddevKnownForN = 0;
+}
+
+double CentralTendencyStatistics::variance() const
+{
+ double variance;
+ if (mVarianceKnownForN != mN) {
+ if (mN > 1) {
+ // double variance_n = M2/n;
+ variance = mM2 / (mN - 1);
+ } else {
+ variance = NAN;
+ }
+ mVariance = variance;
+ mVarianceKnownForN = mN;
+ } else {
+ variance = mVariance;
+ }
+ return variance;
+}
+
+double CentralTendencyStatistics::stddev() const
+{
+ double stddev;
+ if (mStddevKnownForN != mN) {
+ stddev = sqrt(variance());
+ mStddev = stddev;
+ mStddevKnownForN = mN;
+ } else {
+ stddev = mStddev;
+ }
+ return stddev;
+}
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
new file mode 100644
index 0000000..637402a
--- /dev/null
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * 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) {
+ rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs);
+ if (rc) {
+ ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
+ isEnabled = false;
+ } else {
+ mWasEverEnabled = true;
+ // record wall clock time at first enable
+ if (!mMonotonicKnown) {
+ rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
+ if (rc) {
+ ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
+ } else {
+ mMonotonicKnown = true;
+ }
+ }
+ }
+ // disabling
+ } else {
+ struct timespec ts;
+ rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+ if (rc) {
+ ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
+ } else {
+ long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
+ (ts.tv_nsec - mPreviousTs.tv_nsec);
+ mAccumulator += delta;
+#if 0
+ mPreviousTs = ts;
+#endif
+ }
+ }
+ mIsEnabled = isEnabled;
+ }
+ return wasEnabled;
+}
+
+bool ThreadCpuUsage::sampleAndEnable(double& ns)
+{
+ bool ret;
+ bool wasEverEnabled = mWasEverEnabled;
+ if (enable()) {
+ // already enabled, so add a new sample relative to previous
+ return sample(ns);
+ } else if (wasEverEnabled) {
+ // was disabled, but add sample for accumulated time while enabled
+ ns = (double) mAccumulator;
+ mAccumulator = 0;
+ ALOGV("sampleAndEnable %.0f", ns);
+ return true;
+ } else {
+ // first time called
+ ns = 0.0;
+ ALOGV("sampleAndEnable false");
+ return false;
+ }
+}
+
+bool ThreadCpuUsage::sample(double &ns)
+{
+ if (mWasEverEnabled) {
+ if (mIsEnabled) {
+ struct timespec ts;
+ int rc;
+ 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);
+ mAccumulator += delta;
+ mPreviousTs = ts;
+ }
+ } else {
+ mWasEverEnabled = false;
+ }
+ 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;
+ }
+}
+
+long long ThreadCpuUsage::elapsed() const
+{
+ long long elapsed;
+ if (mMonotonicKnown) {
+ struct timespec ts;
+ int rc;
+ rc = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (rc) {
+ ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
+ elapsed = 0;
+ } else {
+ // mMonotonicTs is updated only at first enable and resetStatistics
+ elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL +
+ (ts.tv_nsec - mMonotonicTs.tv_nsec);
+ }
+ } else {
+ ALOGW("Can't compute elapsed time because measurements have never been enabled");
+ elapsed = 0;
+ }
+ ALOGV("elapsed %lld", elapsed);
+ return elapsed;
+}
+
+void ThreadCpuUsage::resetElapsed()
+{
+ ALOGV("resetElapsed");
+ if (mMonotonicKnown) {
+ int rc;
+ rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
+ if (rc) {
+ ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
+ mMonotonicKnown = false;
+ }
+ }
+}
+
+/*static*/
+int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU];
+pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT;
+int ThreadCpuUsage::sKernelMax;
+pthread_mutex_t ThreadCpuUsage::sMutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*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");
+ }
+ int i;
+ for (i = 0; i < MAX_CPU; ++i) {
+ sScalingFds[i] = -1;
+ }
+}
+
+uint32_t ThreadCpuUsage::getCpukHz(int cpuNum)
+{
+ if (cpuNum < 0 || cpuNum >= MAX_CPU) {
+ ALOGW("getCpukHz called with invalid CPU %d", cpuNum);
+ return 0;
+ }
+ // double-checked locking idiom is not broken for atomic values such as fd
+ int fd = sScalingFds[cpuNum];
+ if (fd < 0) {
+ // some kernels can't open a scaling file until hot plug complete
+ pthread_mutex_lock(&sMutex);
+ fd = sScalingFds[cpuNum];
+ if (fd < 0) {
+#define FREQ_SIZE 64
+ char freq_path[FREQ_SIZE];
+#define FREQ_DIGIT 27
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
+#define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq"
+ strlcpy(freq_path, FREQ_PATH, sizeof(freq_path));
+ freq_path[FREQ_DIGIT] = cpuNum + '0';
+ fd = open(freq_path, O_RDONLY | O_CLOEXEC);
+ // keep this fd until process exit or exec
+ sScalingFds[cpuNum] = fd;
+ }
+ pthread_mutex_unlock(&sMutex);
+ if (fd < 0) {
+ ALOGW("getCpukHz can't open 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