blob: 5493b3cf5868757561424928771fc80b0fe2c07a [file] [log] [blame]
Andy Hungcf10d742020-04-28 15:38:24 -07001/*
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
22namespace 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 */
40class ThreadMetrics final {
41public:
42 ThreadMetrics(std::string metricsId, bool isOut)
43 : mMetricsId(std::move(metricsId))
44 , mIsOut(isOut)
45 {}
46
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
59 void logBeginInterval() {
60 std::lock_guard l(mLock);
Andy Hungea840382020-05-05 21:50:17 -070061 // 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) {
Andy Hungcf10d742020-04-28 15:38:24 -070064 deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
Andy Hungea840382020-05-05 21:50:17 -070065 mDevices = patchDevices; // set after endAudioIntervalGroup
Andy Hungcf10d742020-04-28 15:38:24 -070066 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
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
Andy Hungea840382020-05-05 21:50:17 -070085 void logCreatePatch(const std::string& inDevices, const std::string& outDevices) {
Andy Hungcf10d742020-04-28 15:38:24 -070086 std::lock_guard l(mLock);
Andy Hungea840382020-05-05 21:50:17 -070087 mCreatePatchInDevices = inDevices;
88 mCreatePatchOutDevices = outDevices;
Andy Hungcf10d742020-04-28 15:38:24 -070089 mediametrics::LogItem(mMetricsId)
90 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
Andy Hungea840382020-05-05 21:50:17 -070091 .set(AMEDIAMETRICS_PROP_INPUTDEVICES, inDevices)
92 .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, outDevices)
Andy Hungcf10d742020-04-28 15:38:24 -070093 .record();
94 }
95
96 // Called when we are removed from the Thread.
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
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
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
Andy Hungea840382020-05-05 21:50:17 -0700122 void logUnderrunFrames(size_t frames) {
Andy Hungcf10d742020-04-28 15:38:24 -0700123 std::lock_guard l(mLock);
Andy Hungea840382020-05-05 21:50:17 -0700124 if (mLastUnderrun == false && frames > 0) {
125 ++mUnderrunCount; // count non-continguous underrun sequences.
126 }
127 mLastUnderrun = (frames > 0);
128 mUnderrunFrames += frames;
Andy Hungcf10d742020-04-28 15:38:24 -0700129 }
130
131 const std::string& getMetricsId() const {
132 return mMetricsId;
133 }
134
135private:
136 // no lock required - all arguments and constants.
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
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)
Andy Hung76adef72022-09-14 17:24:19 -0700151 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount)
152 // we set "last" device to indicate the device the group was
153 // associated with (because a createPatch which is logged in ThreadMetrics
154 // could have changed the device).
155 .set(mIsOut
156 ? AMEDIAMETRICS_PROP_PREFIX_LAST AMEDIAMETRICS_PROP_OUTPUTDEVICES
157 : AMEDIAMETRICS_PROP_PREFIX_LAST AMEDIAMETRICS_PROP_INPUTDEVICES,
158 mDevices.c_str());
Andy Hungcf10d742020-04-28 15:38:24 -0700159 if (mDeviceLatencyMs.getN() > 0) {
160 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean());
161 }
162 if (mUnderrunCount > 0) {
163 item.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)mUnderrunCount)
164 .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES, (int64_t)mUnderrunFrames);
165 }
166 item.record();
167 }
168 }
169
170 void resetIntervalGroupMetrics() REQUIRES(mLock) {
171 // mDevices is not reset by clear
172
173 mIntervalCount = 0;
174 mIntervalStartTimeNs = 0;
175 // mCumulativeTimeNs is not reset by clear.
176 mDeviceTimeNs = 0;
177
178 mDeviceLatencyMs.reset();
179
Andy Hungea840382020-05-05 21:50:17 -0700180 mLastUnderrun = false;
Andy Hungcf10d742020-04-28 15:38:24 -0700181 mUnderrunCount = 0;
182 mUnderrunFrames = 0;
183 }
184
185 const std::string mMetricsId;
186 const bool mIsOut; // if true, than a playback track, otherwise used for record.
187
188 mutable std::mutex mLock;
189
190 // Devices in the interval group.
Andy Hungea840382020-05-05 21:50:17 -0700191 std::string mDevices GUARDED_BY(mLock); // last input or output devices based on mIsOut.
192 std::string mCreatePatchInDevices GUARDED_BY(mLock);
193 std::string mCreatePatchOutDevices GUARDED_BY(mLock);
Andy Hungcf10d742020-04-28 15:38:24 -0700194
195 // Number of intervals and playing time
196 int32_t mIntervalCount GUARDED_BY(mLock) = 0;
197 int64_t mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
198 int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0;
199 int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
200
201 // latency and startup for each interval.
202 audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
203
204 // underrun count and frames
Andy Hungea840382020-05-05 21:50:17 -0700205 bool mLastUnderrun GUARDED_BY(mLock) = false; // checks consecutive underruns
206 int64_t mUnderrunCount GUARDED_BY(mLock) = 0; // number of consecutive underruns
207 int64_t mUnderrunFrames GUARDED_BY(mLock) = 0; // total estimated frames underrun
Andy Hungcf10d742020-04-28 15:38:24 -0700208};
209
210} // namespace android
211
212#endif // ANDROID_AUDIO_THREADMETRICS_H