1 /*
2  * Copyright (C) 2020 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 #ifndef ANDROID_AUDIO_THREADMETRICS_H
18 #define ANDROID_AUDIO_THREADMETRICS_H
19 
20 #include <mutex>
21 
22 namespace android {
23 
24 /**
25  * ThreadMetrics handles the AudioFlinger thread log statistics.
26  *
27  * We aggregate metrics for a particular device for proper analysis.
28  * This includes power, performance, and usage metrics.
29  *
30  * This class is thread-safe with a lock for safety.  There is no risk of deadlock
31  * as this class only executes external one-way calls in Mediametrics and does not
32  * call any other AudioFlinger class.
33  *
34  * Terminology:
35  * An AudioInterval is a contiguous playback segment.
36  * An AudioIntervalGroup is a group of continuous playback segments on the same device.
37  *
38  * We currently deliver metrics based on an AudioIntervalGroup.
39  */
40 class ThreadMetrics final {
41 public:
ThreadMetrics(std::string metricsId,bool isOut)42     ThreadMetrics(std::string metricsId, bool isOut)
43         : mMetricsId(std::move(metricsId))
44         , mIsOut(isOut)
45         {}
46 
~ThreadMetrics()47     ~ThreadMetrics() {
48         logEndInterval(); // close any open interval groups
49         std::lock_guard l(mLock);
50         deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
51         mediametrics::LogItem(mMetricsId)
52             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
53             .record();
54     }
55 
56     // Called under the following circumstances
57     // 1) Upon a createPatch and we are not in standby
58     // 2) We come out of standby
logBeginInterval()59     void logBeginInterval() {
60         std::lock_guard l(mLock);
61         // The devices we look for change depend on whether the Thread is input or output.
62         const std::string& patchDevices = mIsOut ? mCreatePatchOutDevices : mCreatePatchInDevices;
63         if (mDevices != patchDevices) {
64             deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
65             mDevices = patchDevices; // set after endAudioIntervalGroup
66             resetIntervalGroupMetrics();
67             deliverDeviceMetrics(
68                     AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, mDevices.c_str());
69         }
70         if (mIntervalStartTimeNs == 0) {
71             ++mIntervalCount;
72             mIntervalStartTimeNs = systemTime();
73         }
74     }
75 
logConstructor(pid_t pid,const char * threadType,int32_t id)76     void logConstructor(pid_t pid, const char *threadType, int32_t id) const {
77         mediametrics::LogItem(mMetricsId)
78             .setPid(pid)
79             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
80             .set(AMEDIAMETRICS_PROP_TYPE, threadType)
81             .set(AMEDIAMETRICS_PROP_THREADID, id)
82             .record();
83     }
84 
logCreatePatch(const std::string & inDevices,const std::string & outDevices)85     void logCreatePatch(const std::string& inDevices, const std::string& outDevices) {
86         std::lock_guard l(mLock);
87         mCreatePatchInDevices = inDevices;
88         mCreatePatchOutDevices = outDevices;
89         mediametrics::LogItem(mMetricsId)
90             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
91             .set(AMEDIAMETRICS_PROP_INPUTDEVICES, inDevices)
92             .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, outDevices)
93             .record();
94     }
95 
96     // Called when we are removed from the Thread.
logEndInterval()97     void logEndInterval() {
98         std::lock_guard l(mLock);
99         if (mIntervalStartTimeNs != 0) {
100             const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
101             mIntervalStartTimeNs = 0;
102             mCumulativeTimeNs += elapsedTimeNs;
103             mDeviceTimeNs += elapsedTimeNs;
104         }
105     }
106 
logThrottleMs(double throttleMs)107     void logThrottleMs(double throttleMs) const {
108         mediametrics::LogItem(mMetricsId)
109             // ms units always double
110             .set(AMEDIAMETRICS_PROP_THROTTLEMS, (double)throttleMs)
111             .record();
112     }
113 
logLatency(double latencyMs)114     void logLatency(double latencyMs) {
115         mediametrics::LogItem(mMetricsId)
116             .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
117             .record();
118         std::lock_guard l(mLock);
119         mDeviceLatencyMs.add(latencyMs);
120     }
121 
logUnderrunFrames(size_t frames)122     void logUnderrunFrames(size_t frames) {
123         std::lock_guard l(mLock);
124         if (mLastUnderrun == false && frames > 0) {
125             ++mUnderrunCount; // count non-continguous underrun sequences.
126         }
127         mLastUnderrun = (frames > 0);
128         mUnderrunFrames += frames;
129     }
130 
getMetricsId()131     const std::string& getMetricsId() const {
132         return mMetricsId;
133     }
134 
135 private:
136     // no lock required - all arguments and constants.
deliverDeviceMetrics(const char * eventName,const char * devices)137     void deliverDeviceMetrics(const char *eventName, const char *devices) const {
138         mediametrics::LogItem(mMetricsId)
139             .set(AMEDIAMETRICS_PROP_EVENT, eventName)
140             .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
141                    : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
142            .record();
143     }
144 
deliverCumulativeMetrics(const char * eventName)145     void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
146         if (mIntervalCount > 0) {
147             mediametrics::LogItem item(mMetricsId);
148             item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
149                 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
150                 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
151                 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount);
152             if (mDeviceLatencyMs.getN() > 0) {
153                 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean());
154             }
155             if (mUnderrunCount > 0) {
156                 item.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)mUnderrunCount)
157                     .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES, (int64_t)mUnderrunFrames);
158             }
159             item.record();
160         }
161     }
162 
resetIntervalGroupMetrics()163     void resetIntervalGroupMetrics() REQUIRES(mLock) {
164         // mDevices is not reset by clear
165 
166         mIntervalCount = 0;
167         mIntervalStartTimeNs = 0;
168         // mCumulativeTimeNs is not reset by clear.
169         mDeviceTimeNs = 0;
170 
171         mDeviceLatencyMs.reset();
172 
173         mLastUnderrun = false;
174         mUnderrunCount = 0;
175         mUnderrunFrames = 0;
176     }
177 
178     const std::string mMetricsId;
179     const bool        mIsOut;  // if true, than a playback track, otherwise used for record.
180 
181     mutable           std::mutex mLock;
182 
183     // Devices in the interval group.
184     std::string       mDevices GUARDED_BY(mLock); // last input or output devices based on mIsOut.
185     std::string       mCreatePatchInDevices GUARDED_BY(mLock);
186     std::string       mCreatePatchOutDevices GUARDED_BY(mLock);
187 
188     // Number of intervals and playing time
189     int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
190     int64_t           mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
191     int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0;
192     int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;
193 
194     // latency and startup for each interval.
195     audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
196 
197     // underrun count and frames
198     bool              mLastUnderrun GUARDED_BY(mLock) = false; // checks consecutive underruns
199     int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;    // number of consecutive underruns
200     int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;   // total estimated frames underrun
201 };
202 
203 } // namespace android
204 
205 #endif // ANDROID_AUDIO_THREADMETRICS_H
206