1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "ThreadCpuUsage" 18 //#define LOG_NDEBUG 0 19 20 #include <errno.h> 21 #include <stdlib.h> 22 #include <time.h> 23 24 #include <utils/Debug.h> 25 #include <utils/Log.h> 26 27 #include <cpustats/ThreadCpuUsage.h> 28 29 namespace android { 30 31 bool ThreadCpuUsage::setEnabled(bool isEnabled) 32 { 33 bool wasEnabled = mIsEnabled; 34 // only do something if there is a change 35 if (isEnabled != wasEnabled) { 36 ALOGV("setEnabled(%d)", isEnabled); 37 int rc; 38 // enabling 39 if (isEnabled) { 40 rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs); 41 if (rc) { 42 ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); 43 isEnabled = false; 44 } else { 45 mWasEverEnabled = true; 46 // record wall clock time at first enable 47 if (!mMonotonicKnown) { 48 rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); 49 if (rc) { 50 ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); 51 } else { 52 mMonotonicKnown = true; 53 } 54 } 55 } 56 // disabling 57 } else { 58 struct timespec ts; 59 rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); 60 if (rc) { 61 ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); 62 } else { 63 long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + 64 (ts.tv_nsec - mPreviousTs.tv_nsec); 65 mAccumulator += delta; 66 #if 0 67 mPreviousTs = ts; 68 #endif 69 } 70 } 71 mIsEnabled = isEnabled; 72 } 73 return wasEnabled; 74 } 75 76 bool ThreadCpuUsage::sampleAndEnable(double& ns) 77 { 78 bool ret; 79 bool wasEverEnabled = mWasEverEnabled; 80 if (enable()) { 81 // already enabled, so add a new sample relative to previous 82 return sample(ns); 83 } else if (wasEverEnabled) { 84 // was disabled, but add sample for accumulated time while enabled 85 ns = (double) mAccumulator; 86 mAccumulator = 0; 87 ALOGV("sampleAndEnable %.0f", ns); 88 return true; 89 } else { 90 // first time called 91 ns = 0.0; 92 ALOGV("sampleAndEnable false"); 93 return false; 94 } 95 } 96 97 bool ThreadCpuUsage::sample(double &ns) 98 { 99 if (mWasEverEnabled) { 100 if (mIsEnabled) { 101 struct timespec ts; 102 int rc; 103 rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); 104 if (rc) { 105 ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); 106 ns = 0.0; 107 return false; 108 } else { 109 long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + 110 (ts.tv_nsec - mPreviousTs.tv_nsec); 111 mAccumulator += delta; 112 mPreviousTs = ts; 113 } 114 } else { 115 mWasEverEnabled = false; 116 } 117 ns = (double) mAccumulator; 118 ALOGV("sample %.0f", ns); 119 mAccumulator = 0; 120 return true; 121 } else { 122 ALOGW("Can't add sample because measurements have never been enabled"); 123 ns = 0.0; 124 return false; 125 } 126 } 127 128 long long ThreadCpuUsage::elapsed() const 129 { 130 long long elapsed; 131 if (mMonotonicKnown) { 132 struct timespec ts; 133 int rc; 134 rc = clock_gettime(CLOCK_MONOTONIC, &ts); 135 if (rc) { 136 ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); 137 elapsed = 0; 138 } else { 139 // mMonotonicTs is updated only at first enable and resetStatistics 140 elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL + 141 (ts.tv_nsec - mMonotonicTs.tv_nsec); 142 } 143 } else { 144 ALOGW("Can't compute elapsed time because measurements have never been enabled"); 145 elapsed = 0; 146 } 147 ALOGV("elapsed %lld", elapsed); 148 return elapsed; 149 } 150 151 void ThreadCpuUsage::resetElapsed() 152 { 153 ALOGV("resetElapsed"); 154 if (mMonotonicKnown) { 155 int rc; 156 rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); 157 if (rc) { 158 ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); 159 mMonotonicKnown = false; 160 } 161 } 162 } 163 164 /*static*/ 165 int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU]; 166 pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT; 167 int ThreadCpuUsage::sKernelMax; 168 pthread_mutex_t ThreadCpuUsage::sMutex = PTHREAD_MUTEX_INITIALIZER; 169 170 /*static*/ 171 void ThreadCpuUsage::init() 172 { 173 // read the number of CPUs 174 sKernelMax = 1; 175 int fd = open("/sys/devices/system/cpu/kernel_max", O_RDONLY); 176 if (fd >= 0) { 177 #define KERNEL_MAX_SIZE 12 178 char kernelMax[KERNEL_MAX_SIZE]; 179 ssize_t actual = read(fd, kernelMax, sizeof(kernelMax)); 180 if (actual >= 2 && kernelMax[actual-1] == '\n') { 181 sKernelMax = atoi(kernelMax); 182 if (sKernelMax >= MAX_CPU - 1) { 183 ALOGW("kernel_max %d but MAX_CPU %d", sKernelMax, MAX_CPU); 184 sKernelMax = MAX_CPU; 185 } else if (sKernelMax < 0) { 186 ALOGW("kernel_max invalid %d", sKernelMax); 187 sKernelMax = 1; 188 } else { 189 ++sKernelMax; 190 ALOGV("number of CPUs %d", sKernelMax); 191 } 192 } else { 193 ALOGW("Can't read number of CPUs"); 194 } 195 (void) close(fd); 196 } else { 197 ALOGW("Can't open number of CPUs"); 198 } 199 int i; 200 for (i = 0; i < MAX_CPU; ++i) { 201 sScalingFds[i] = -1; 202 } 203 } 204 205 uint32_t ThreadCpuUsage::getCpukHz(int cpuNum) 206 { 207 if (cpuNum < 0 || cpuNum >= MAX_CPU) { 208 ALOGW("getCpukHz called with invalid CPU %d", cpuNum); 209 return 0; 210 } 211 // double-checked locking idiom is not broken for atomic values such as fd 212 int fd = sScalingFds[cpuNum]; 213 if (fd < 0) { 214 // some kernels can't open a scaling file until hot plug complete 215 pthread_mutex_lock(&sMutex); 216 fd = sScalingFds[cpuNum]; 217 if (fd < 0) { 218 #define FREQ_SIZE 64 219 char freq_path[FREQ_SIZE]; 220 #define FREQ_DIGIT 27 221 COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10); 222 #define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq" 223 strlcpy(freq_path, FREQ_PATH, sizeof(freq_path)); 224 freq_path[FREQ_DIGIT] = cpuNum + '0'; 225 fd = open(freq_path, O_RDONLY | O_CLOEXEC); 226 // keep this fd until process exit or exec 227 sScalingFds[cpuNum] = fd; 228 } 229 pthread_mutex_unlock(&sMutex); 230 if (fd < 0) { 231 ALOGW("getCpukHz can't open CPU %d", cpuNum); 232 return 0; 233 } 234 } 235 #define KHZ_SIZE 12 236 char kHz[KHZ_SIZE]; // kHz base 10 237 ssize_t actual = pread(fd, kHz, sizeof(kHz), (off_t) 0); 238 uint32_t ret; 239 if (actual >= 2 && kHz[actual-1] == '\n') { 240 ret = atoi(kHz); 241 } else { 242 ret = 0; 243 } 244 if (ret != mCurrentkHz[cpuNum]) { 245 if (ret > 0) { 246 ALOGV("CPU %d frequency %u kHz", cpuNum, ret); 247 } else { 248 ALOGW("Can't read CPU %d frequency", cpuNum); 249 } 250 mCurrentkHz[cpuNum] = ret; 251 } 252 return ret; 253 } 254 255 } // namespace android 256