1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker *
4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker *
8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker *
10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker */
16*ec779b8eSAndroid Build Coastguard Worker
17*ec779b8eSAndroid Build Coastguard Worker
18*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "PerformanceAnalysis"
19*ec779b8eSAndroid Build Coastguard Worker // #define LOG_NDEBUG 0
20*ec779b8eSAndroid Build Coastguard Worker // #define WRITE_TO_FILE
21*ec779b8eSAndroid Build Coastguard Worker
22*ec779b8eSAndroid Build Coastguard Worker #include <algorithm>
23*ec779b8eSAndroid Build Coastguard Worker #include <climits>
24*ec779b8eSAndroid Build Coastguard Worker #include <deque>
25*ec779b8eSAndroid Build Coastguard Worker #include <iomanip>
26*ec779b8eSAndroid Build Coastguard Worker #include <math.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <numeric>
28*ec779b8eSAndroid Build Coastguard Worker #include <sstream>
29*ec779b8eSAndroid Build Coastguard Worker #include <string>
30*ec779b8eSAndroid Build Coastguard Worker #include <vector>
31*ec779b8eSAndroid Build Coastguard Worker #include <stdarg.h>
32*ec779b8eSAndroid Build Coastguard Worker #include <stdint.h>
33*ec779b8eSAndroid Build Coastguard Worker #include <stdio.h>
34*ec779b8eSAndroid Build Coastguard Worker #include <string.h>
35*ec779b8eSAndroid Build Coastguard Worker #include <sys/prctl.h>
36*ec779b8eSAndroid Build Coastguard Worker #include <time.h>
37*ec779b8eSAndroid Build Coastguard Worker #include <new>
38*ec779b8eSAndroid Build Coastguard Worker #include <audio_utils/LogPlot.h>
39*ec779b8eSAndroid Build Coastguard Worker #include <audio_utils/roundup.h>
40*ec779b8eSAndroid Build Coastguard Worker #include <media/nblog/NBLog.h>
41*ec779b8eSAndroid Build Coastguard Worker #include <media/nblog/PerformanceAnalysis.h>
42*ec779b8eSAndroid Build Coastguard Worker #include <media/nblog/ReportPerformance.h>
43*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
44*ec779b8eSAndroid Build Coastguard Worker #include <utils/String8.h>
45*ec779b8eSAndroid Build Coastguard Worker #include <utils/Timers.h>
46*ec779b8eSAndroid Build Coastguard Worker
47*ec779b8eSAndroid Build Coastguard Worker #include <queue>
48*ec779b8eSAndroid Build Coastguard Worker #include <utility>
49*ec779b8eSAndroid Build Coastguard Worker
50*ec779b8eSAndroid Build Coastguard Worker namespace android {
51*ec779b8eSAndroid Build Coastguard Worker namespace ReportPerformance {
52*ec779b8eSAndroid Build Coastguard Worker
add(double value)53*ec779b8eSAndroid Build Coastguard Worker void Histogram::add(double value)
54*ec779b8eSAndroid Build Coastguard Worker {
55*ec779b8eSAndroid Build Coastguard Worker if (mBinSize <= 0 || mBins.size() < 2) {
56*ec779b8eSAndroid Build Coastguard Worker return;
57*ec779b8eSAndroid Build Coastguard Worker }
58*ec779b8eSAndroid Build Coastguard Worker // TODO Handle domain and range error exceptions?
59*ec779b8eSAndroid Build Coastguard Worker const int unboundedIndex = lround((value - mLow) / mBinSize) + 1;
60*ec779b8eSAndroid Build Coastguard Worker // std::clamp is introduced in C++17
61*ec779b8eSAndroid Build Coastguard Worker //const int index = std::clamp(unboundedIndex, 0, (int)(mBins.size() - 1));
62*ec779b8eSAndroid Build Coastguard Worker const int index = std::max(0, std::min((int)(mBins.size() - 1), unboundedIndex));
63*ec779b8eSAndroid Build Coastguard Worker mBins[index]++;
64*ec779b8eSAndroid Build Coastguard Worker mTotalCount++;
65*ec779b8eSAndroid Build Coastguard Worker }
66*ec779b8eSAndroid Build Coastguard Worker
clear()67*ec779b8eSAndroid Build Coastguard Worker void Histogram::clear()
68*ec779b8eSAndroid Build Coastguard Worker {
69*ec779b8eSAndroid Build Coastguard Worker std::fill(mBins.begin(), mBins.end(), 0);
70*ec779b8eSAndroid Build Coastguard Worker mTotalCount = 0;
71*ec779b8eSAndroid Build Coastguard Worker }
72*ec779b8eSAndroid Build Coastguard Worker
totalCount() const73*ec779b8eSAndroid Build Coastguard Worker uint64_t Histogram::totalCount() const
74*ec779b8eSAndroid Build Coastguard Worker {
75*ec779b8eSAndroid Build Coastguard Worker return mTotalCount;
76*ec779b8eSAndroid Build Coastguard Worker }
77*ec779b8eSAndroid Build Coastguard Worker
toString() const78*ec779b8eSAndroid Build Coastguard Worker std::string Histogram::toString() const {
79*ec779b8eSAndroid Build Coastguard Worker std::stringstream ss;
80*ec779b8eSAndroid Build Coastguard Worker static constexpr char kDivider = '|';
81*ec779b8eSAndroid Build Coastguard Worker ss << kVersion << "," << mBinSize << "," << mNumBins << "," << mLow << ",{";
82*ec779b8eSAndroid Build Coastguard Worker bool first = true;
83*ec779b8eSAndroid Build Coastguard Worker for (size_t i = 0; i < mBins.size(); i++) {
84*ec779b8eSAndroid Build Coastguard Worker if (mBins[i] != 0) {
85*ec779b8eSAndroid Build Coastguard Worker if (!first) {
86*ec779b8eSAndroid Build Coastguard Worker ss << ",";
87*ec779b8eSAndroid Build Coastguard Worker }
88*ec779b8eSAndroid Build Coastguard Worker ss << static_cast<int>(i) - 1 << kDivider << mBins[i];
89*ec779b8eSAndroid Build Coastguard Worker first = false;
90*ec779b8eSAndroid Build Coastguard Worker }
91*ec779b8eSAndroid Build Coastguard Worker }
92*ec779b8eSAndroid Build Coastguard Worker ss << "}";
93*ec779b8eSAndroid Build Coastguard Worker
94*ec779b8eSAndroid Build Coastguard Worker return ss.str();
95*ec779b8eSAndroid Build Coastguard Worker }
96*ec779b8eSAndroid Build Coastguard Worker
asciiArtString(size_t indent) const97*ec779b8eSAndroid Build Coastguard Worker std::string Histogram::asciiArtString(size_t indent) const {
98*ec779b8eSAndroid Build Coastguard Worker if (totalCount() == 0 || mBinSize <= 0 || mBins.size() < 2) {
99*ec779b8eSAndroid Build Coastguard Worker return "";
100*ec779b8eSAndroid Build Coastguard Worker }
101*ec779b8eSAndroid Build Coastguard Worker
102*ec779b8eSAndroid Build Coastguard Worker static constexpr char kMarker = '-';
103*ec779b8eSAndroid Build Coastguard Worker // One increment is considered one step of a bin's height.
104*ec779b8eSAndroid Build Coastguard Worker static constexpr size_t kMarkersPerIncrement = 2;
105*ec779b8eSAndroid Build Coastguard Worker static constexpr size_t kMaxIncrements = 64 + 1;
106*ec779b8eSAndroid Build Coastguard Worker static constexpr size_t kMaxNumberWidth = 7;
107*ec779b8eSAndroid Build Coastguard Worker static const std::string kMarkers(kMarkersPerIncrement * kMaxIncrements, kMarker);
108*ec779b8eSAndroid Build Coastguard Worker static const std::string kSpaces(kMarkersPerIncrement * kMaxIncrements, ' ');
109*ec779b8eSAndroid Build Coastguard Worker // get the last n characters of s, or the whole string if it is shorter
110*ec779b8eSAndroid Build Coastguard Worker auto getTail = [](const size_t n, const std::string &s) {
111*ec779b8eSAndroid Build Coastguard Worker return s.c_str() + s.size() - std::min(n, s.size());
112*ec779b8eSAndroid Build Coastguard Worker };
113*ec779b8eSAndroid Build Coastguard Worker
114*ec779b8eSAndroid Build Coastguard Worker // Since totalCount() > 0, mBins is not empty and maxCount > 0.
115*ec779b8eSAndroid Build Coastguard Worker const unsigned maxCount = *std::max_element(mBins.begin(), mBins.end());
116*ec779b8eSAndroid Build Coastguard Worker const size_t maxIncrements = log2(maxCount) + 1;
117*ec779b8eSAndroid Build Coastguard Worker
118*ec779b8eSAndroid Build Coastguard Worker std::stringstream ss;
119*ec779b8eSAndroid Build Coastguard Worker
120*ec779b8eSAndroid Build Coastguard Worker // Non-zero bins must exist at this point because totalCount() > 0.
121*ec779b8eSAndroid Build Coastguard Worker size_t firstNonZeroBin = 0;
122*ec779b8eSAndroid Build Coastguard Worker // If firstNonZeroBin reaches mBins.size() - 1, then it must be a nonzero bin.
123*ec779b8eSAndroid Build Coastguard Worker for (; firstNonZeroBin < mBins.size() - 1 && mBins[firstNonZeroBin] == 0; firstNonZeroBin++) {}
124*ec779b8eSAndroid Build Coastguard Worker const size_t firstBinToPrint = firstNonZeroBin == 0 ? 0 : firstNonZeroBin - 1;
125*ec779b8eSAndroid Build Coastguard Worker
126*ec779b8eSAndroid Build Coastguard Worker size_t lastNonZeroBin = mBins.size() - 1;
127*ec779b8eSAndroid Build Coastguard Worker // If lastNonZeroBin reaches 0, then it must be a nonzero bin.
128*ec779b8eSAndroid Build Coastguard Worker for (; lastNonZeroBin > 0 && mBins[lastNonZeroBin] == 0; lastNonZeroBin--) {}
129*ec779b8eSAndroid Build Coastguard Worker const size_t lastBinToPrint = lastNonZeroBin == mBins.size() - 1 ? lastNonZeroBin
130*ec779b8eSAndroid Build Coastguard Worker : lastNonZeroBin + 1;
131*ec779b8eSAndroid Build Coastguard Worker
132*ec779b8eSAndroid Build Coastguard Worker for (size_t bin = firstBinToPrint; bin <= lastBinToPrint; bin++) {
133*ec779b8eSAndroid Build Coastguard Worker ss << std::setw(indent + kMaxNumberWidth);
134*ec779b8eSAndroid Build Coastguard Worker if (bin == 0) {
135*ec779b8eSAndroid Build Coastguard Worker ss << "<";
136*ec779b8eSAndroid Build Coastguard Worker } else if (bin == mBins.size() - 1) {
137*ec779b8eSAndroid Build Coastguard Worker ss << ">";
138*ec779b8eSAndroid Build Coastguard Worker } else {
139*ec779b8eSAndroid Build Coastguard Worker ss << mLow + (bin - 1) * mBinSize;
140*ec779b8eSAndroid Build Coastguard Worker }
141*ec779b8eSAndroid Build Coastguard Worker ss << " |";
142*ec779b8eSAndroid Build Coastguard Worker size_t increments = 0;
143*ec779b8eSAndroid Build Coastguard Worker const uint64_t binCount = mBins[bin];
144*ec779b8eSAndroid Build Coastguard Worker if (binCount > 0) {
145*ec779b8eSAndroid Build Coastguard Worker increments = log2(binCount) + 1;
146*ec779b8eSAndroid Build Coastguard Worker ss << getTail(increments * kMarkersPerIncrement, kMarkers);
147*ec779b8eSAndroid Build Coastguard Worker }
148*ec779b8eSAndroid Build Coastguard Worker ss << getTail((maxIncrements - increments + 1) * kMarkersPerIncrement, kSpaces)
149*ec779b8eSAndroid Build Coastguard Worker << binCount << "\n";
150*ec779b8eSAndroid Build Coastguard Worker }
151*ec779b8eSAndroid Build Coastguard Worker ss << "\n";
152*ec779b8eSAndroid Build Coastguard Worker
153*ec779b8eSAndroid Build Coastguard Worker return ss.str();
154*ec779b8eSAndroid Build Coastguard Worker }
155*ec779b8eSAndroid Build Coastguard Worker
156*ec779b8eSAndroid Build Coastguard Worker //------------------------------------------------------------------------------
157*ec779b8eSAndroid Build Coastguard Worker
158*ec779b8eSAndroid Build Coastguard Worker // Given an audio processing wakeup timestamp, buckets the time interval
159*ec779b8eSAndroid Build Coastguard Worker // since the previous timestamp into a histogram, searches for
160*ec779b8eSAndroid Build Coastguard Worker // outliers, analyzes the outlier series for unexpectedly
161*ec779b8eSAndroid Build Coastguard Worker // small or large values and stores these as peaks
logTsEntry(timestamp ts)162*ec779b8eSAndroid Build Coastguard Worker void PerformanceAnalysis::logTsEntry(timestamp ts) {
163*ec779b8eSAndroid Build Coastguard Worker // after a state change, start a new series and do not
164*ec779b8eSAndroid Build Coastguard Worker // record time intervals in-between
165*ec779b8eSAndroid Build Coastguard Worker if (mBufferPeriod.mPrevTs == 0) {
166*ec779b8eSAndroid Build Coastguard Worker mBufferPeriod.mPrevTs = ts;
167*ec779b8eSAndroid Build Coastguard Worker return;
168*ec779b8eSAndroid Build Coastguard Worker }
169*ec779b8eSAndroid Build Coastguard Worker
170*ec779b8eSAndroid Build Coastguard Worker // calculate time interval between current and previous timestamp
171*ec779b8eSAndroid Build Coastguard Worker const msInterval diffMs = static_cast<msInterval>(
172*ec779b8eSAndroid Build Coastguard Worker deltaMs(mBufferPeriod.mPrevTs, ts));
173*ec779b8eSAndroid Build Coastguard Worker
174*ec779b8eSAndroid Build Coastguard Worker const int diffJiffy = deltaJiffy(mBufferPeriod.mPrevTs, ts);
175*ec779b8eSAndroid Build Coastguard Worker
176*ec779b8eSAndroid Build Coastguard Worker // old versus new weight ratio when updating the buffer period mean
177*ec779b8eSAndroid Build Coastguard Worker static constexpr double exponentialWeight = 0.999;
178*ec779b8eSAndroid Build Coastguard Worker // update buffer period mean with exponential weighting
179*ec779b8eSAndroid Build Coastguard Worker mBufferPeriod.mMean = (mBufferPeriod.mMean < 0) ? diffMs :
180*ec779b8eSAndroid Build Coastguard Worker exponentialWeight * mBufferPeriod.mMean + (1.0 - exponentialWeight) * diffMs;
181*ec779b8eSAndroid Build Coastguard Worker // set mOutlierFactor to a smaller value for the fastmixer thread
182*ec779b8eSAndroid Build Coastguard Worker const int kFastMixerMax = 10;
183*ec779b8eSAndroid Build Coastguard Worker // NormalMixer times vary much more than FastMixer times.
184*ec779b8eSAndroid Build Coastguard Worker // TODO: mOutlierFactor values are set empirically based on what appears to be
185*ec779b8eSAndroid Build Coastguard Worker // an outlier. Learn these values from the data.
186*ec779b8eSAndroid Build Coastguard Worker mBufferPeriod.mOutlierFactor = mBufferPeriod.mMean < kFastMixerMax ? 1.8 : 2.0;
187*ec779b8eSAndroid Build Coastguard Worker // set outlier threshold
188*ec779b8eSAndroid Build Coastguard Worker mBufferPeriod.mOutlier = mBufferPeriod.mMean * mBufferPeriod.mOutlierFactor;
189*ec779b8eSAndroid Build Coastguard Worker
190*ec779b8eSAndroid Build Coastguard Worker // Check whether the time interval between the current timestamp
191*ec779b8eSAndroid Build Coastguard Worker // and the previous one is long enough to count as an outlier
192*ec779b8eSAndroid Build Coastguard Worker const bool isOutlier = detectAndStoreOutlier(diffMs);
193*ec779b8eSAndroid Build Coastguard Worker // If an outlier was found, check whether it was a peak
194*ec779b8eSAndroid Build Coastguard Worker if (isOutlier) {
195*ec779b8eSAndroid Build Coastguard Worker /*bool isPeak =*/ detectAndStorePeak(
196*ec779b8eSAndroid Build Coastguard Worker mOutlierData[0].first, mOutlierData[0].second);
197*ec779b8eSAndroid Build Coastguard Worker // TODO: decide whether to insert a new empty histogram if a peak
198*ec779b8eSAndroid Build Coastguard Worker // TODO: remove isPeak if unused to avoid "unused variable" error
199*ec779b8eSAndroid Build Coastguard Worker // occurred at the current timestamp
200*ec779b8eSAndroid Build Coastguard Worker }
201*ec779b8eSAndroid Build Coastguard Worker
202*ec779b8eSAndroid Build Coastguard Worker // Insert a histogram to mHists if it is empty, or
203*ec779b8eSAndroid Build Coastguard Worker // close the current histogram and insert a new empty one if
204*ec779b8eSAndroid Build Coastguard Worker // if the current histogram has spanned its maximum time interval.
205*ec779b8eSAndroid Build Coastguard Worker if (mHists.empty() ||
206*ec779b8eSAndroid Build Coastguard Worker deltaMs(mHists[0].first, ts) >= kMaxLength.HistTimespanMs) {
207*ec779b8eSAndroid Build Coastguard Worker mHists.emplace_front(ts, std::map<int, int>());
208*ec779b8eSAndroid Build Coastguard Worker // When memory is full, delete oldest histogram
209*ec779b8eSAndroid Build Coastguard Worker // TODO: use a circular buffer
210*ec779b8eSAndroid Build Coastguard Worker if (mHists.size() >= kMaxLength.Hists) {
211*ec779b8eSAndroid Build Coastguard Worker mHists.resize(kMaxLength.Hists);
212*ec779b8eSAndroid Build Coastguard Worker }
213*ec779b8eSAndroid Build Coastguard Worker }
214*ec779b8eSAndroid Build Coastguard Worker // add current time intervals to histogram
215*ec779b8eSAndroid Build Coastguard Worker ++mHists[0].second[diffJiffy];
216*ec779b8eSAndroid Build Coastguard Worker // update previous timestamp
217*ec779b8eSAndroid Build Coastguard Worker mBufferPeriod.mPrevTs = ts;
218*ec779b8eSAndroid Build Coastguard Worker }
219*ec779b8eSAndroid Build Coastguard Worker
220*ec779b8eSAndroid Build Coastguard Worker
221*ec779b8eSAndroid Build Coastguard Worker // forces short-term histogram storage to avoid adding idle audio time interval
222*ec779b8eSAndroid Build Coastguard Worker // to buffer period data
handleStateChange()223*ec779b8eSAndroid Build Coastguard Worker void PerformanceAnalysis::handleStateChange() {
224*ec779b8eSAndroid Build Coastguard Worker mBufferPeriod.mPrevTs = 0;
225*ec779b8eSAndroid Build Coastguard Worker return;
226*ec779b8eSAndroid Build Coastguard Worker }
227*ec779b8eSAndroid Build Coastguard Worker
228*ec779b8eSAndroid Build Coastguard Worker
229*ec779b8eSAndroid Build Coastguard Worker // Checks whether the time interval between two outliers is far enough from
230*ec779b8eSAndroid Build Coastguard Worker // a typical delta to be considered a peak.
231*ec779b8eSAndroid Build Coastguard Worker // looks for changes in distribution (peaks), which can be either positive or negative.
232*ec779b8eSAndroid Build Coastguard Worker // The function sets the mean to the starting value and sigma to 0, and updates
233*ec779b8eSAndroid Build Coastguard Worker // them as long as no peak is detected. When a value is more than 'threshold'
234*ec779b8eSAndroid Build Coastguard Worker // standard deviations from the mean, a peak is detected and the mean and sigma
235*ec779b8eSAndroid Build Coastguard Worker // are set to the peak value and 0.
detectAndStorePeak(msInterval diff,timestamp ts)236*ec779b8eSAndroid Build Coastguard Worker bool PerformanceAnalysis::detectAndStorePeak(msInterval diff, timestamp ts) {
237*ec779b8eSAndroid Build Coastguard Worker bool isPeak = false;
238*ec779b8eSAndroid Build Coastguard Worker if (mOutlierData.empty()) {
239*ec779b8eSAndroid Build Coastguard Worker return false;
240*ec779b8eSAndroid Build Coastguard Worker }
241*ec779b8eSAndroid Build Coastguard Worker // Update mean of the distribution
242*ec779b8eSAndroid Build Coastguard Worker // TypicalDiff is used to check whether a value is unusually large
243*ec779b8eSAndroid Build Coastguard Worker // when we cannot use standard deviations from the mean because the sd is set to 0.
244*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mTypicalDiff = (mOutlierDistribution.mTypicalDiff *
245*ec779b8eSAndroid Build Coastguard Worker (mOutlierData.size() - 1) + diff) / mOutlierData.size();
246*ec779b8eSAndroid Build Coastguard Worker
247*ec779b8eSAndroid Build Coastguard Worker // Initialize short-term mean at start of program
248*ec779b8eSAndroid Build Coastguard Worker if (mOutlierDistribution.mMean == 0) {
249*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mMean = diff;
250*ec779b8eSAndroid Build Coastguard Worker }
251*ec779b8eSAndroid Build Coastguard Worker // Update length of current sequence of outliers
252*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mN++;
253*ec779b8eSAndroid Build Coastguard Worker
254*ec779b8eSAndroid Build Coastguard Worker // Check whether a large deviation from the mean occurred.
255*ec779b8eSAndroid Build Coastguard Worker // If the standard deviation has been reset to zero, the comparison is
256*ec779b8eSAndroid Build Coastguard Worker // instead to the mean of the full mOutlierInterval sequence.
257*ec779b8eSAndroid Build Coastguard Worker if ((fabs(diff - mOutlierDistribution.mMean) <
258*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.kMaxDeviation * mOutlierDistribution.mSd) ||
259*ec779b8eSAndroid Build Coastguard Worker (mOutlierDistribution.mSd == 0 &&
260*ec779b8eSAndroid Build Coastguard Worker fabs(diff - mOutlierDistribution.mMean) <
261*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mTypicalDiff)) {
262*ec779b8eSAndroid Build Coastguard Worker // update the mean and sd using online algorithm
263*ec779b8eSAndroid Build Coastguard Worker // https://en.wikipedia.org/wiki/
264*ec779b8eSAndroid Build Coastguard Worker // Algorithms_for_calculating_variance#Online_algorithm
265*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mN++;
266*ec779b8eSAndroid Build Coastguard Worker const double kDelta = diff - mOutlierDistribution.mMean;
267*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mMean += kDelta / mOutlierDistribution.mN;
268*ec779b8eSAndroid Build Coastguard Worker const double kDelta2 = diff - mOutlierDistribution.mMean;
269*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mM2 += kDelta * kDelta2;
270*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mSd = (mOutlierDistribution.mN < 2) ? 0 :
271*ec779b8eSAndroid Build Coastguard Worker sqrt(mOutlierDistribution.mM2 / (mOutlierDistribution.mN - 1));
272*ec779b8eSAndroid Build Coastguard Worker } else {
273*ec779b8eSAndroid Build Coastguard Worker // new value is far from the mean:
274*ec779b8eSAndroid Build Coastguard Worker // store peak timestamp and reset mean, sd, and short-term sequence
275*ec779b8eSAndroid Build Coastguard Worker isPeak = true;
276*ec779b8eSAndroid Build Coastguard Worker mPeakTimestamps.emplace_front(ts);
277*ec779b8eSAndroid Build Coastguard Worker // if mPeaks has reached capacity, delete oldest data
278*ec779b8eSAndroid Build Coastguard Worker // Note: this means that mOutlierDistribution values do not exactly
279*ec779b8eSAndroid Build Coastguard Worker // match the data we have in mPeakTimestamps, but this is not an issue
280*ec779b8eSAndroid Build Coastguard Worker // in practice for estimating future peaks.
281*ec779b8eSAndroid Build Coastguard Worker // TODO: turn this into a circular buffer
282*ec779b8eSAndroid Build Coastguard Worker if (mPeakTimestamps.size() >= kMaxLength.Peaks) {
283*ec779b8eSAndroid Build Coastguard Worker mPeakTimestamps.resize(kMaxLength.Peaks);
284*ec779b8eSAndroid Build Coastguard Worker }
285*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mMean = 0;
286*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mSd = 0;
287*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mN = 0;
288*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mM2 = 0;
289*ec779b8eSAndroid Build Coastguard Worker }
290*ec779b8eSAndroid Build Coastguard Worker return isPeak;
291*ec779b8eSAndroid Build Coastguard Worker }
292*ec779b8eSAndroid Build Coastguard Worker
293*ec779b8eSAndroid Build Coastguard Worker
294*ec779b8eSAndroid Build Coastguard Worker // Determines whether the difference between a timestamp and the previous
295*ec779b8eSAndroid Build Coastguard Worker // one is beyond a threshold. If yes, stores the timestamp as an outlier
296*ec779b8eSAndroid Build Coastguard Worker // and writes to mOutlierdata in the following format:
297*ec779b8eSAndroid Build Coastguard Worker // Time elapsed since previous outlier: Timestamp of start of outlier
298*ec779b8eSAndroid Build Coastguard Worker // e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
299*ec779b8eSAndroid Build Coastguard Worker // TODO: learn what timestamp sequences correlate with glitches instead of
300*ec779b8eSAndroid Build Coastguard Worker // manually designing a heuristic.
detectAndStoreOutlier(const msInterval diffMs)301*ec779b8eSAndroid Build Coastguard Worker bool PerformanceAnalysis::detectAndStoreOutlier(const msInterval diffMs) {
302*ec779b8eSAndroid Build Coastguard Worker bool isOutlier = false;
303*ec779b8eSAndroid Build Coastguard Worker if (diffMs >= mBufferPeriod.mOutlier) {
304*ec779b8eSAndroid Build Coastguard Worker isOutlier = true;
305*ec779b8eSAndroid Build Coastguard Worker mOutlierData.emplace_front(
306*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mElapsed, mBufferPeriod.mPrevTs);
307*ec779b8eSAndroid Build Coastguard Worker // Remove oldest value if the vector is full
308*ec779b8eSAndroid Build Coastguard Worker // TODO: turn this into a circular buffer
309*ec779b8eSAndroid Build Coastguard Worker // TODO: make sure kShortHistSize is large enough that that data will never be lost
310*ec779b8eSAndroid Build Coastguard Worker // before being written to file or to a FIFO
311*ec779b8eSAndroid Build Coastguard Worker if (mOutlierData.size() >= kMaxLength.Outliers) {
312*ec779b8eSAndroid Build Coastguard Worker mOutlierData.resize(kMaxLength.Outliers);
313*ec779b8eSAndroid Build Coastguard Worker }
314*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mElapsed = 0;
315*ec779b8eSAndroid Build Coastguard Worker }
316*ec779b8eSAndroid Build Coastguard Worker mOutlierDistribution.mElapsed += diffMs;
317*ec779b8eSAndroid Build Coastguard Worker return isOutlier;
318*ec779b8eSAndroid Build Coastguard Worker }
319*ec779b8eSAndroid Build Coastguard Worker
320*ec779b8eSAndroid Build Coastguard Worker // rounds value to precision based on log-distance from mean
321*ec779b8eSAndroid Build Coastguard Worker __attribute__((no_sanitize("signed-integer-overflow")))
logRound(double x,double mean)322*ec779b8eSAndroid Build Coastguard Worker inline double logRound(double x, double mean) {
323*ec779b8eSAndroid Build Coastguard Worker // Larger values decrease range of high resolution and prevent overflow
324*ec779b8eSAndroid Build Coastguard Worker // of a histogram on the console.
325*ec779b8eSAndroid Build Coastguard Worker // The following formula adjusts kBase based on the buffer period length.
326*ec779b8eSAndroid Build Coastguard Worker // Different threads have buffer periods ranging from 2 to 40. The
327*ec779b8eSAndroid Build Coastguard Worker // formula below maps buffer period 2 to kBase = ~1, 4 to ~2, 20 to ~3, 40 to ~4.
328*ec779b8eSAndroid Build Coastguard Worker // TODO: tighten this for higher means, the data still overflows
329*ec779b8eSAndroid Build Coastguard Worker const double kBase = log(mean) / log(2.2);
330*ec779b8eSAndroid Build Coastguard Worker const double power = floor(
331*ec779b8eSAndroid Build Coastguard Worker log(abs(x - mean) / mean) / log(kBase)) + 2;
332*ec779b8eSAndroid Build Coastguard Worker // do not round values close to the mean
333*ec779b8eSAndroid Build Coastguard Worker if (power < 1) {
334*ec779b8eSAndroid Build Coastguard Worker return x;
335*ec779b8eSAndroid Build Coastguard Worker }
336*ec779b8eSAndroid Build Coastguard Worker const int factor = static_cast<int>(pow(10, power));
337*ec779b8eSAndroid Build Coastguard Worker return (static_cast<int>(x) * factor) / factor;
338*ec779b8eSAndroid Build Coastguard Worker }
339*ec779b8eSAndroid Build Coastguard Worker
340*ec779b8eSAndroid Build Coastguard Worker // TODO Make it return a std::string instead of modifying body
341*ec779b8eSAndroid Build Coastguard Worker // TODO: move this to ReportPerformance, probably make it a friend function
342*ec779b8eSAndroid Build Coastguard Worker // of PerformanceAnalysis
reportPerformance(String8 * body,int author,log_hash_t hash,int maxHeight)343*ec779b8eSAndroid Build Coastguard Worker void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_t hash,
344*ec779b8eSAndroid Build Coastguard Worker int maxHeight) {
345*ec779b8eSAndroid Build Coastguard Worker if (mHists.empty() || body == nullptr) {
346*ec779b8eSAndroid Build Coastguard Worker return;
347*ec779b8eSAndroid Build Coastguard Worker }
348*ec779b8eSAndroid Build Coastguard Worker
349*ec779b8eSAndroid Build Coastguard Worker // ms of active audio in displayed histogram
350*ec779b8eSAndroid Build Coastguard Worker double elapsedMs = 0;
351*ec779b8eSAndroid Build Coastguard Worker // starting timestamp of histogram
352*ec779b8eSAndroid Build Coastguard Worker timestamp startingTs = mHists[0].first;
353*ec779b8eSAndroid Build Coastguard Worker
354*ec779b8eSAndroid Build Coastguard Worker // histogram which stores .1 precision ms counts instead of Jiffy multiple counts
355*ec779b8eSAndroid Build Coastguard Worker std::map<double, int> buckets;
356*ec779b8eSAndroid Build Coastguard Worker for (const auto &shortHist: mHists) {
357*ec779b8eSAndroid Build Coastguard Worker for (const auto &countPair : shortHist.second) {
358*ec779b8eSAndroid Build Coastguard Worker const double ms = static_cast<double>(countPair.first) / kJiffyPerMs;
359*ec779b8eSAndroid Build Coastguard Worker buckets[logRound(ms, mBufferPeriod.mMean)] += countPair.second;
360*ec779b8eSAndroid Build Coastguard Worker elapsedMs += ms * countPair.second;
361*ec779b8eSAndroid Build Coastguard Worker }
362*ec779b8eSAndroid Build Coastguard Worker }
363*ec779b8eSAndroid Build Coastguard Worker
364*ec779b8eSAndroid Build Coastguard Worker static const int SIZE = 128;
365*ec779b8eSAndroid Build Coastguard Worker char title[SIZE];
366*ec779b8eSAndroid Build Coastguard Worker snprintf(title, sizeof(title), "\n%s %3.2f %s\n%s%d, %lld, %lld\n",
367*ec779b8eSAndroid Build Coastguard Worker "Occurrences in", (elapsedMs / kMsPerSec), "seconds of audio:",
368*ec779b8eSAndroid Build Coastguard Worker "Thread, hash, starting timestamp: ", author,
369*ec779b8eSAndroid Build Coastguard Worker static_cast<long long>(hash), static_cast<long long>(startingTs));
370*ec779b8eSAndroid Build Coastguard Worker static const char * const kLabel = "ms";
371*ec779b8eSAndroid Build Coastguard Worker
372*ec779b8eSAndroid Build Coastguard Worker body->appendFormat("%s",
373*ec779b8eSAndroid Build Coastguard Worker audio_utils_plot_histogram(buckets, title, kLabel, maxHeight).c_str());
374*ec779b8eSAndroid Build Coastguard Worker
375*ec779b8eSAndroid Build Coastguard Worker // Now report glitches
376*ec779b8eSAndroid Build Coastguard Worker body->appendFormat("\ntime elapsed between glitches and glitch timestamps:\n");
377*ec779b8eSAndroid Build Coastguard Worker for (const auto &outlier: mOutlierData) {
378*ec779b8eSAndroid Build Coastguard Worker body->appendFormat("%lld: %lld\n", static_cast<long long>(outlier.first),
379*ec779b8eSAndroid Build Coastguard Worker static_cast<long long>(outlier.second));
380*ec779b8eSAndroid Build Coastguard Worker }
381*ec779b8eSAndroid Build Coastguard Worker }
382*ec779b8eSAndroid Build Coastguard Worker
383*ec779b8eSAndroid Build Coastguard Worker //------------------------------------------------------------------------------
384*ec779b8eSAndroid Build Coastguard Worker
385*ec779b8eSAndroid Build Coastguard Worker // writes summary of performance into specified file descriptor
dump(int fd,int indent,PerformanceAnalysisMap & threadPerformanceAnalysis)386*ec779b8eSAndroid Build Coastguard Worker void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) {
387*ec779b8eSAndroid Build Coastguard Worker String8 body;
388*ec779b8eSAndroid Build Coastguard Worker #ifdef WRITE_TO_FILE
389*ec779b8eSAndroid Build Coastguard Worker const char* const kDirectory = "/data/misc/audioserver/";
390*ec779b8eSAndroid Build Coastguard Worker #endif
391*ec779b8eSAndroid Build Coastguard Worker for (auto & thread : threadPerformanceAnalysis) {
392*ec779b8eSAndroid Build Coastguard Worker for (auto & hash: thread.second) {
393*ec779b8eSAndroid Build Coastguard Worker PerformanceAnalysis& curr = hash.second;
394*ec779b8eSAndroid Build Coastguard Worker // write performance data to console
395*ec779b8eSAndroid Build Coastguard Worker curr.reportPerformance(&body, thread.first, hash.first);
396*ec779b8eSAndroid Build Coastguard Worker if (!body.empty()) {
397*ec779b8eSAndroid Build Coastguard Worker dumpLine(fd, indent, body);
398*ec779b8eSAndroid Build Coastguard Worker body.clear();
399*ec779b8eSAndroid Build Coastguard Worker }
400*ec779b8eSAndroid Build Coastguard Worker #ifdef WRITE_TO_FILE
401*ec779b8eSAndroid Build Coastguard Worker // write to file. Enable by uncommenting macro at top of file.
402*ec779b8eSAndroid Build Coastguard Worker writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps,
403*ec779b8eSAndroid Build Coastguard Worker kDirectory, false, thread.first, hash.first);
404*ec779b8eSAndroid Build Coastguard Worker #endif
405*ec779b8eSAndroid Build Coastguard Worker }
406*ec779b8eSAndroid Build Coastguard Worker }
407*ec779b8eSAndroid Build Coastguard Worker }
408*ec779b8eSAndroid Build Coastguard Worker
409*ec779b8eSAndroid Build Coastguard Worker
410*ec779b8eSAndroid Build Coastguard Worker // Writes a string into specified file descriptor
dumpLine(int fd,int indent,const String8 & body)411*ec779b8eSAndroid Build Coastguard Worker void dumpLine(int fd, int indent, const String8 &body) {
412*ec779b8eSAndroid Build Coastguard Worker dprintf(fd, "%.*s%s \n", indent, "", body.c_str());
413*ec779b8eSAndroid Build Coastguard Worker }
414*ec779b8eSAndroid Build Coastguard Worker
415*ec779b8eSAndroid Build Coastguard Worker } // namespace ReportPerformance
416*ec779b8eSAndroid Build Coastguard Worker } // namespace android
417