blob: 652665535ff172993516e515f747b36deeaa4dca [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)
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
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
Andy Hungea840382020-05-05 21:50:17 -0700173 mLastUnderrun = false;
Andy Hungcf10d742020-04-28 15:38:24 -0700174 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.
Andy Hungea840382020-05-05 21:50:17 -0700184 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);
Andy Hungcf10d742020-04-28 15:38:24 -0700187
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
Andy Hungea840382020-05-05 21:50:17 -0700198 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
Andy Hungcf10d742020-04-28 15:38:24 -0700201};
202
203} // namespace android
204
205#endif // ANDROID_AUDIO_THREADMETRICS_H