blob: 84b76356af20551764fb7b37c06f11afff4e0f24 [file] [log] [blame]
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -07001/*
2 * Copyright (C) 2017 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
18#define LOG_TAG "PerformanceAnalysis"
19// #define LOG_NDEBUG 0
20
21#include <algorithm>
22#include <climits>
23#include <deque>
24#include <fstream>
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -070025#include <iostream>
26#include <math.h>
27#include <numeric>
28#include <vector>
29#include <stdarg.h>
30#include <stdint.h>
31#include <stdio.h>
32#include <string.h>
33#include <sys/prctl.h>
34#include <time.h>
35#include <new>
36#include <audio_utils/roundup.h>
37#include <media/nbaio/NBLog.h>
38#include <media/nbaio/PerformanceAnalysis.h>
39// #include <utils/CallStack.h> // used to print callstack
40#include <utils/Log.h>
41#include <utils/String8.h>
42
43#include <queue>
44#include <utility>
45
46namespace android {
47
48PerformanceAnalysis::PerformanceAnalysis() : findGlitch(false) {
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -070049 kPeriodMsCPU = static_cast<int>(PerformanceAnalysis::kPeriodMs * kRatio);
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -070050}
51
52static int widthOf(int x) {
53 int width = 0;
54 while (x > 0) {
55 ++width;
56 x /= 10;
57 }
58 return width;
59}
60
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -070061// FIXME: delete this temporary test code, recycled for various new functions
62void PerformanceAnalysis::testFunction() {
63 // produces values (4: 5000000), (13: 18000000)
64 // ns timestamps of buffer periods
65 const std::vector<int64_t>kTempTestData = {1000000, 4000000, 5000000,
66 16000000, 18000000, 28000000};
67 const int kTestAuthor = 1;
68 PerformanceAnalysis::storeOutlierData(kTestAuthor, kTempTestData);
69 for (const auto &outlier: mOutlierData) {
70 ALOGE("PerformanceAnalysis test %lld: %lld",
71 static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
72 }
73}
74
75// Each pair consists of: <outlier timestamp, time elapsed since previous outlier>.
76// The timestamp of the beginning of the outlier is recorded.
77// The elapsed time is from the timestamp of the previous outlier
78// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
79// This function is applied to the time series before it is converted into a histogram.
80void PerformanceAnalysis::storeOutlierData(
81 int author, const std::vector<int64_t> &timestamps) {
82 if (timestamps.size() < 1) {
83 ALOGE("storeOutlierData called on empty vector");
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -070084 return;
85 }
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -070086 author++; // temp to avoid unused error until this value is
87 // either TODO: used or discarded from the arglist
88 author--;
89 uint64_t elapsed = 0;
90 int64_t prev = timestamps.at(0);
91 for (const auto &ts: timestamps) {
92 const uint64_t diff = static_cast<uint64_t>(deltaMs(prev, ts));
93 if (diff >= static_cast<uint64_t>(kOutlierMs)) {
94 mOutlierData.emplace_back(elapsed, static_cast<uint64_t>(prev));
95 elapsed = 0;
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -070096 }
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -070097 elapsed += diff;
98 prev = ts;
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -070099 }
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -0700100 // ALOGE("storeOutlierData: result length %zu", outlierData.size());
101 // for (const auto &outlier: OutlierData) {
102 // ALOGE("PerformanceAnalysis test %lld: %lld",
103 // static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
104 //}
105}
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -0700106
107// TODO: implement peak detector
108/*
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -0700109 static void peakDetector() {
110 return;
111 } */
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -0700112
113// TODO put this function in separate file. Make it return a std::string instead of modifying body
114// TODO create a subclass of Reader for this and related work
115// FIXME: as can be seen when printing the values, the outlier timestamps typically occur
116// in the first histogram 35 to 38 indices from the end (most often 35).
117// TODO: build histogram buckets earlier and discard timestamps to save memory
118// TODO consider changing all ints to uint32_t or uint64_t
119void PerformanceAnalysis::reportPerformance(String8 *body,
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -0700120 const std::deque<std::pair
121 <int, short_histogram>> &shortHists,
122 int maxHeight) {
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -0700123 if (shortHists.size() < 1) {
124 return;
125 }
126 // this is temporary code, which only prints out one histogram
127 // of all data stored in buffer. The data is not erased, only overwritten.
128 // TODO: more elaborate data analysis
129 std::map<int, int> buckets;
130 for (const auto &shortHist: shortHists) {
131 for (const auto &countPair : shortHist.second) {
132 buckets[countPair.first] += countPair.second;
133 }
134 }
135
136 // underscores and spaces length corresponds to maximum width of histogram
137 static const int kLen = 40;
138 std::string underscores(kLen, '_');
139 std::string spaces(kLen, ' ');
140
141 auto it = buckets.begin();
142 int maxDelta = it->first;
143 int maxCount = it->second;
144 // Compute maximum values
145 while (++it != buckets.end()) {
146 if (it->first > maxDelta) {
147 maxDelta = it->first;
148 }
149 if (it->second > maxCount) {
150 maxCount = it->second;
151 }
152 }
153 int height = log2(maxCount) + 1; // maxCount > 0, safe to call log2
154 const int leftPadding = widthOf(1 << height);
155 const int colWidth = std::max(std::max(widthOf(maxDelta) + 1, 3), leftPadding + 2);
156 int scalingFactor = 1;
157 // scale data if it exceeds maximum height
158 if (height > maxHeight) {
159 scalingFactor = (height + maxHeight) / maxHeight;
160 height /= scalingFactor;
161 }
162 body->appendFormat("\n%*s", leftPadding + 11, "Occurrences");
163 // write histogram label line with bucket values
164 body->appendFormat("\n%s", " ");
165 body->appendFormat("%*s", leftPadding, " ");
166 for (auto const &x : buckets) {
167 body->appendFormat("%*d", colWidth, x.second);
168 }
169 // write histogram ascii art
170 body->appendFormat("\n%s", " ");
171 for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) {
172 const int value = 1 << row;
173 body->appendFormat("%.*s", leftPadding, spaces.c_str());
174 for (auto const &x : buckets) {
175 body->appendFormat("%.*s%s", colWidth - 1, spaces.c_str(), x.second < value ? " " : "|");
176 }
177 body->appendFormat("\n%s", " ");
178 }
179 // print x-axis
180 const int columns = static_cast<int>(buckets.size());
181 body->appendFormat("%*c", leftPadding, ' ');
182 body->appendFormat("%.*s", (columns + 1) * colWidth, underscores.c_str());
183 body->appendFormat("\n%s", " ");
184
185 // write footer with bucket labels
186 body->appendFormat("%*s", leftPadding, " ");
187 for (auto const &x : buckets) {
188 body->appendFormat("%*d", colWidth, x.first);
189 }
190 body->appendFormat("%.*s%s", colWidth, spaces.c_str(), "ms\n");
191
Sanna Catherine de Treville Wager316f1fd2017-06-23 09:10:15 -0700192 // Now report glitches
193 body->appendFormat("\ntime elapsed between glitches and glitch timestamps\n");
194 for (const auto &outlier: mOutlierData) {
195 body->appendFormat("%lld: %lld\n", static_cast<long long>(outlier.first),
196 static_cast<long long>(outlier.second));
197 }
198
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -0700199}
200
201
202// Produces a log warning if the timing of recent buffer periods caused a glitch
203// Computes sum of running window of three buffer periods
204// Checks whether the buffer periods leave enough CPU time for the next one
205// e.g. if a buffer period is expected to be 4 ms and a buffer requires 3 ms of CPU time,
206// here are some glitch cases:
207// 4 + 4 + 6 ; 5 + 4 + 5; 2 + 2 + 10
208// TODO: develop this code to track changes in histogram distribution in addition
209// to / instead of glitches.
210void PerformanceAnalysis::alertIfGlitch(const std::vector<int64_t> &samples) {
211 std::deque<int> periods(kNumBuff, kPeriodMs);
212 for (size_t i = 2; i < samples.size(); ++i) { // skip first time entry
213 periods.push_front(deltaMs(samples[i - 1], samples[i]));
214 periods.pop_back();
215 // TODO: check that all glitch cases are covered
216 if (std::accumulate(periods.begin(), periods.end(), 0) > kNumBuff * kPeriodMs +
217 kPeriodMs - kPeriodMsCPU) {
218 ALOGW("A glitch occurred");
219 periods.assign(kNumBuff, kPeriodMs);
220 }
221 }
222 return;
223}
224
225bool PerformanceAnalysis::isFindGlitch() const
226{
227 return findGlitch;
228}
229
230void PerformanceAnalysis::setFindGlitch(bool s)
231{
232 findGlitch = s;
233}
Sanna Catherine de Treville Wagerd0dfe432017-06-22 15:09:38 -0700234
235} // namespace android