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