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