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
setEnabled(bool isEnabled)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
sampleAndEnable(double & ns)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
sample(double & ns)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
elapsed() const128 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
resetElapsed()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*/
init()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
getCpukHz(int cpuNum)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