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