Home | History | Annotate | Download | only in libcpustats
      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 
     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 
     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 
     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 
    128 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 
    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*/
    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 
    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