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