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 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 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 
sample(double & ns)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 
elapsed() const127 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 
resetElapsed()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*/
init()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 
getCpukHz(int cpuNum)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