Skip to content

Commit 81fe4fb

Browse files
gkastenAndroid (Google) Code Review
authored andcommitted
Merge "AudioFlinger playback thread CPU measurement in Hz"
2 parents 1f1bc8b + f57e2bc commit 81fe4fb

File tree

3 files changed

+254
-55
lines changed

3 files changed

+254
-55
lines changed

include/cpustats/ThreadCpuUsage.h

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@
1717
#ifndef _THREAD_CPU_USAGE_H
1818
#define _THREAD_CPU_USAGE_H
1919

20-
#include <cpustats/CentralTendencyStatistics.h>
20+
#include <fcntl.h>
21+
#include <pthread.h>
2122

22-
// Track CPU usage for the current thread, and maintain statistics on
23-
// the CPU usage. Units are in per-thread CPU ns, as reported by
23+
namespace android {
24+
25+
// Track CPU usage for the current thread.
26+
// Units are in per-thread CPU ns, as reported by
2427
// clock_gettime(CLOCK_THREAD_CPUTIME_ID). Simple usage: for cyclic
2528
// threads where you want to measure the execution time of the whole
2629
// cycle, just call sampleAndEnable() at the start of each cycle.
27-
// Then call statistics() to get the results, and resetStatistics()
28-
// to start a new set of measurements.
29-
// For acyclic threads, or for cyclic threads where you want to measure
30+
// For acyclic threads, or for cyclic threads where you want to measure/track
3031
// only part of each cycle, call enable(), disable(), and/or setEnabled()
3132
// to demarcate the region(s) of interest, and then call sample() periodically.
3233
// This class is not thread-safe for concurrent calls from multiple threads;
@@ -44,13 +45,17 @@ class ThreadCpuUsage
4445
// mPreviousTs
4546
// mMonotonicTs
4647
mMonotonicKnown(false)
47-
// mStatistics
48-
{ }
48+
{
49+
(void) pthread_once(&sOnceControl, &init);
50+
for (int i = 0; i < sKernelMax; ++i) {
51+
mCurrentkHz[i] = (uint32_t) ~0; // unknown
52+
}
53+
}
4954

5055
~ThreadCpuUsage() { }
5156

5257
// Return whether currently tracking CPU usage by current thread
53-
bool isEnabled() { return mIsEnabled; }
58+
bool isEnabled() const { return mIsEnabled; }
5459

5560
// Enable tracking of CPU usage by current thread;
5661
// any CPU used from this point forward will be tracked.
@@ -66,39 +71,52 @@ class ThreadCpuUsage
6671
// This method is intended to be used for safe nested enable/disabling.
6772
bool setEnabled(bool isEnabled);
6873

69-
// Add a sample point for central tendency statistics, and also
70-
// enable tracking if needed. If tracking has never been enabled, then
71-
// enables tracking but does not add a sample (it is not possible to add
72-
// a sample the first time because no previous). Otherwise if tracking is
73-
// enabled, then adds a sample for tracked CPU ns since the previous
74+
// Add a sample point, and also enable tracking if needed.
75+
// If tracking has never been enabled, then this call enables tracking but
76+
// does _not_ add a sample -- it is not possible to add a sample the
77+
// first time because there is no previous point to subtract from.
78+
// Otherwise, if tracking is enabled,
79+
// then adds a sample for tracked CPU ns since the previous
7480
// sample, or since the first call to sampleAndEnable(), enable(), or
7581
// setEnabled(true). If there was a previous sample but tracking is
7682
// now disabled, then adds a sample for the tracked CPU ns accumulated
7783
// up until the most recent disable(), resets this accumulator, and then
7884
// enables tracking. Calling this method rather than enable() followed
7985
// by sample() avoids a race condition for the first sample.
80-
void sampleAndEnable();
86+
// Returns true if the sample 'ns' is valid, or false if invalid.
87+
// Note that 'ns' is an output parameter passed by reference.
88+
// The caller does not need to initialize this variable.
89+
// The units are CPU nanoseconds consumed by current thread.
90+
bool sampleAndEnable(double& ns);
8191

82-
// Add a sample point for central tendency statistics, but do not
92+
// Add a sample point, but do not
8393
// change the tracking enabled status. If tracking has either never been
8494
// enabled, or has never been enabled since the last sample, then log a warning
8595
// and don't add sample. Otherwise, adds a sample for tracked CPU ns since
8696
// the previous sample or since the first call to sampleAndEnable(),
8797
// enable(), or setEnabled(true) if no previous sample.
88-
void sample();
98+
// Returns true if the sample is valid, or false if invalid.
99+
// Note that 'ns' is an output parameter passed by reference.
100+
// The caller does not need to initialize this variable.
101+
// The units are CPU nanoseconds consumed by current thread.
102+
bool sample(double& ns);
89103

90-
// Return the elapsed delta wall clock ns since initial enable or statistics reset,
104+
// Return the elapsed delta wall clock ns since initial enable or reset,
91105
// as reported by clock_gettime(CLOCK_MONOTONIC).
92106
long long elapsed() const;
93107

94-
// Reset statistics and elapsed. Has no effect on tracking or accumulator.
95-
void resetStatistics();
108+
// Reset elapsed wall clock. Has no effect on tracking or accumulator.
109+
void resetElapsed();
96110

97-
// Return a const reference to the central tendency statistics.
98-
// Note that only the const methods can be called on this object.
99-
const CentralTendencyStatistics& statistics() const {
100-
return mStatistics;
101-
}
111+
// Return current clock frequency for specified CPU, in kHz.
112+
// You can get your CPU number using sched_getcpu(2). Note that, unless CPU affinity
113+
// has been configured appropriately, the CPU number can change.
114+
// Also note that, unless the CPU governor has been configured appropriately,
115+
// the CPU frequency can change. And even if the CPU frequency is locked down
116+
// to a particular value, that the frequency might still be adjusted
117+
// to prevent thermal overload. Therefore you should poll for your thread's
118+
// current CPU number and clock frequency periodically.
119+
uint32_t getCpukHz(int cpuNum);
102120

103121
private:
104122
bool mIsEnabled; // whether tracking is currently enabled
@@ -107,7 +125,15 @@ class ThreadCpuUsage
107125
struct timespec mPreviousTs; // most recent thread CPU time, valid only if mIsEnabled is true
108126
struct timespec mMonotonicTs; // most recent monotonic time
109127
bool mMonotonicKnown; // whether mMonotonicTs has been set
110-
CentralTendencyStatistics mStatistics;
128+
129+
static const int MAX_CPU = 8;
130+
static int sScalingFds[MAX_CPU];// file descriptor per CPU for reading scaling_cur_freq
131+
uint32_t mCurrentkHz[MAX_CPU]; // current CPU frequency in kHz, not static to avoid a race
132+
static pthread_once_t sOnceControl;
133+
static int sKernelMax; // like MAX_CPU, but determined at runtime == cpu/kernel_max + 1
134+
static void init();
111135
};
112136

137+
} // namespace android
138+
113139
#endif // _THREAD_CPU_USAGE_H

libs/cpustats/ThreadCpuUsage.cpp

Lines changed: 120 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,26 @@
1414
* limitations under the License.
1515
*/
1616

17+
#define LOG_TAG "ThreadCpuUsage"
18+
//#define LOG_NDEBUG 0
19+
1720
#include <errno.h>
21+
#include <stdlib.h>
1822
#include <time.h>
1923

24+
#include <utils/Debug.h>
2025
#include <utils/Log.h>
2126

2227
#include <cpustats/ThreadCpuUsage.h>
2328

29+
namespace android {
30+
2431
bool ThreadCpuUsage::setEnabled(bool isEnabled)
2532
{
2633
bool wasEnabled = mIsEnabled;
2734
// only do something if there is a change
2835
if (isEnabled != wasEnabled) {
36+
ALOGV("setEnabled(%d)", isEnabled);
2937
int rc;
3038
// enabling
3139
if (isEnabled) {
@@ -65,20 +73,28 @@ bool ThreadCpuUsage::setEnabled(bool isEnabled)
6573
return wasEnabled;
6674
}
6775

68-
void ThreadCpuUsage::sampleAndEnable()
76+
bool ThreadCpuUsage::sampleAndEnable(double& ns)
6977
{
78+
bool ret;
7079
bool wasEverEnabled = mWasEverEnabled;
7180
if (enable()) {
7281
// already enabled, so add a new sample relative to previous
73-
sample();
82+
return sample(ns);
7483
} else if (wasEverEnabled) {
7584
// was disabled, but add sample for accumulated time while enabled
76-
mStatistics.sample((double) mAccumulator);
85+
ns = (double) mAccumulator;
7786
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;
7894
}
7995
}
8096

81-
void ThreadCpuUsage::sample()
97+
bool ThreadCpuUsage::sample(double &ns)
8298
{
8399
if (mWasEverEnabled) {
84100
if (mIsEnabled) {
@@ -87,6 +103,8 @@ void ThreadCpuUsage::sample()
87103
rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
88104
if (rc) {
89105
ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
106+
ns = 0.0;
107+
return false;
90108
} else {
91109
long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
92110
(ts.tv_nsec - mPreviousTs.tv_nsec);
@@ -96,10 +114,14 @@ void ThreadCpuUsage::sample()
96114
} else {
97115
mWasEverEnabled = false;
98116
}
99-
mStatistics.sample((double) mAccumulator);
117+
ns = (double) mAccumulator;
118+
ALOGV("sample %.0f", ns);
100119
mAccumulator = 0;
120+
return true;
101121
} else {
102122
ALOGW("Can't add sample because measurements have never been enabled");
123+
ns = 0.0;
124+
return false;
103125
}
104126
}
105127

@@ -122,12 +144,13 @@ long long ThreadCpuUsage::elapsed() const
122144
ALOGW("Can't compute elapsed time because measurements have never been enabled");
123145
elapsed = 0;
124146
}
147+
ALOGV("elapsed %lld", elapsed);
125148
return elapsed;
126149
}
127150

128-
void ThreadCpuUsage::resetStatistics()
151+
void ThreadCpuUsage::resetElapsed()
129152
{
130-
mStatistics.reset();
153+
ALOGV("resetElapsed");
131154
if (mMonotonicKnown) {
132155
int rc;
133156
rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
@@ -137,3 +160,93 @@ void ThreadCpuUsage::resetStatistics()
137160
}
138161
}
139162
}
163+
164+
/*static*/
165+
int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU];
166+
pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT;
167+
int ThreadCpuUsage::sKernelMax;
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+
199+
// open fd to each frequency per CPU
200+
#define FREQ_SIZE 64
201+
char freq_path[FREQ_SIZE];
202+
#define FREQ_DIGIT 27
203+
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
204+
strlcpy(freq_path, "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq", sizeof(freq_path));
205+
int i;
206+
for (i = 0; i < MAX_CPU; ++i) {
207+
sScalingFds[i] = -1;
208+
}
209+
for (i = 0; i < sKernelMax; ++i) {
210+
freq_path[FREQ_DIGIT] = i + '0';
211+
fd = open(freq_path, O_RDONLY);
212+
if (fd >= 0) {
213+
// keep this fd until process exit
214+
sScalingFds[i] = fd;
215+
} else {
216+
ALOGW("Can't open CPU %d", i);
217+
}
218+
}
219+
}
220+
221+
uint32_t ThreadCpuUsage::getCpukHz(int cpuNum)
222+
{
223+
if (cpuNum < 0 || cpuNum >= MAX_CPU) {
224+
ALOGW("getCpukHz called with invalid CPU %d", cpuNum);
225+
return 0;
226+
}
227+
int fd = sScalingFds[cpuNum];
228+
if (fd < 0) {
229+
ALOGW("getCpukHz called for unopened CPU %d", cpuNum);
230+
return 0;
231+
}
232+
#define KHZ_SIZE 12
233+
char kHz[KHZ_SIZE]; // kHz base 10
234+
ssize_t actual = pread(fd, kHz, sizeof(kHz), (off_t) 0);
235+
uint32_t ret;
236+
if (actual >= 2 && kHz[actual-1] == '\n') {
237+
ret = atoi(kHz);
238+
} else {
239+
ret = 0;
240+
}
241+
if (ret != mCurrentkHz[cpuNum]) {
242+
if (ret > 0) {
243+
ALOGV("CPU %d frequency %u kHz", cpuNum, ret);
244+
} else {
245+
ALOGW("Can't read CPU %d frequency", cpuNum);
246+
}
247+
mCurrentkHz[cpuNum] = ret;
248+
}
249+
return ret;
250+
}
251+
252+
} // namespace android

0 commit comments

Comments
 (0)