• 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 <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