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