blob: 7989de1fe20aa911362e88b197b9d794fcc20eb4 [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);
61 if (mDevices != mCreatePatchDevices) {
62 deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
63 mDevices = mCreatePatchDevices; // set after endAudioIntervalGroup
64 resetIntervalGroupMetrics();
65 deliverDeviceMetrics(
66 AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, mDevices.c_str());
67 }
68 if (mIntervalStartTimeNs == 0) {
69 ++mIntervalCount;
70 mIntervalStartTimeNs = systemTime();
71 }
72 }
73
74 void logConstructor(pid_t pid, const char *threadType, int32_t id) const {
75 mediametrics::LogItem(mMetricsId)
76 .setPid(pid)
77 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
78 .set(AMEDIAMETRICS_PROP_TYPE, threadType)
79 .set(AMEDIAMETRICS_PROP_THREADID, id)
80 .record();
81 }
82
83 void logCreatePatch(const std::string& devices) {
84 std::lock_guard l(mLock);
85 mCreatePatchDevices = devices;
86 mediametrics::LogItem(mMetricsId)
87 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
88 .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, devices)
89 .record();
90 }
91
92 // Called when we are removed from the Thread.
93 void logEndInterval() {
94 std::lock_guard l(mLock);
95 if (mIntervalStartTimeNs != 0) {
96 const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
97 mIntervalStartTimeNs = 0;
98 mCumulativeTimeNs += elapsedTimeNs;
99 mDeviceTimeNs += elapsedTimeNs;
100 }
101 }
102
103 void logThrottleMs(double throttleMs) const {
104 mediametrics::LogItem(mMetricsId)
105 // ms units always double
106 .set(AMEDIAMETRICS_PROP_THROTTLEMS, (double)throttleMs)
107 .record();
108 }
109
110 void logLatency(double latencyMs) {
111 mediametrics::LogItem(mMetricsId)
112 .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
113 .record();
114 std::lock_guard l(mLock);
115 mDeviceLatencyMs.add(latencyMs);
116 }
117
118 // TODO: further implement this.
119 void logUnderrunFrames(size_t count, size_t frames) {
120 std::lock_guard l(mLock);
121 mUnderrunCount = count;
122 mUnderrunFrames = frames;
123 }
124
125 const std::string& getMetricsId() const {
126 return mMetricsId;
127 }
128
129private:
130 // no lock required - all arguments and constants.
131 void deliverDeviceMetrics(const char *eventName, const char *devices) const {
132 mediametrics::LogItem(mMetricsId)
133 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
134 .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
135 : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
136 .record();
137 }
138
139 void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
140 if (mIntervalCount > 0) {
141 mediametrics::LogItem item(mMetricsId);
142 item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
143 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
144 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
145 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount);
146 if (mDeviceLatencyMs.getN() > 0) {
147 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean());
148 }
149 if (mUnderrunCount > 0) {
150 item.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)mUnderrunCount)
151 .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES, (int64_t)mUnderrunFrames);
152 }
153 item.record();
154 }
155 }
156
157 void resetIntervalGroupMetrics() REQUIRES(mLock) {
158 // mDevices is not reset by clear
159
160 mIntervalCount = 0;
161 mIntervalStartTimeNs = 0;
162 // mCumulativeTimeNs is not reset by clear.
163 mDeviceTimeNs = 0;
164
165 mDeviceLatencyMs.reset();
166
167 mUnderrunCount = 0;
168 mUnderrunFrames = 0;
169 }
170
171 const std::string mMetricsId;
172 const bool mIsOut; // if true, than a playback track, otherwise used for record.
173
174 mutable std::mutex mLock;
175
176 // Devices in the interval group.
177 std::string mDevices GUARDED_BY(mLock);
178 std::string mCreatePatchDevices GUARDED_BY(mLock);
179
180 // Number of intervals and playing time
181 int32_t mIntervalCount GUARDED_BY(mLock) = 0;
182 int64_t mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
183 int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0;
184 int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
185
186 // latency and startup for each interval.
187 audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
188
189 // underrun count and frames
190 int64_t mUnderrunCount GUARDED_BY(mLock) = 0;
191 int64_t mUnderrunFrames GUARDED_BY(mLock) = 0;
192};
193
194} // namespace android
195
196#endif // ANDROID_AUDIO_THREADMETRICS_H