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