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 <string.h>
     23 #include <time.h>
     24 
     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 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 
     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 
    127 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 
    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*/
    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 
    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