Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 1 | /* |
| 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 Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 25 | #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 | |
| 46 | namespace android { |
| 47 | |
| 48 | PerformanceAnalysis::PerformanceAnalysis() : findGlitch(false) { |
Sanna Catherine de Treville Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 49 | kPeriodMsCPU = static_cast<int>(PerformanceAnalysis::kPeriodMs * kRatio); |
Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | static 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 Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 61 | // FIXME: delete this temporary test code, recycled for various new functions |
| 62 | void 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. |
| 80 | void PerformanceAnalysis::storeOutlierData( |
| 81 | int author, const std::vector<int64_t> ×tamps) { |
| 82 | if (timestamps.size() < 1) { |
| 83 | ALOGE("storeOutlierData called on empty vector"); |
Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 84 | return; |
| 85 | } |
Sanna Catherine de Treville Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 86 | 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 Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 96 | } |
Sanna Catherine de Treville Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 97 | elapsed += diff; |
| 98 | prev = ts; |
Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 99 | } |
Sanna Catherine de Treville Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 100 | // 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 Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 106 | |
| 107 | // TODO: implement peak detector |
| 108 | /* |
Sanna Catherine de Treville Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 109 | static void peakDetector() { |
| 110 | return; |
| 111 | } */ |
Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 112 | |
| 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 |
| 119 | void PerformanceAnalysis::reportPerformance(String8 *body, |
Sanna Catherine de Treville Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 120 | const std::deque<std::pair |
| 121 | <int, short_histogram>> &shortHists, |
| 122 | int maxHeight) { |
Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 123 | 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 Wager | 316f1fd | 2017-06-23 09:10:15 -0700 | [diff] [blame^] | 192 | // 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 Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 199 | } |
| 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. |
| 210 | void 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 | |
| 225 | bool PerformanceAnalysis::isFindGlitch() const |
| 226 | { |
| 227 | return findGlitch; |
| 228 | } |
| 229 | |
| 230 | void PerformanceAnalysis::setFindGlitch(bool s) |
| 231 | { |
| 232 | findGlitch = s; |
| 233 | } |
Sanna Catherine de Treville Wager | d0dfe43 | 2017-06-22 15:09:38 -0700 | [diff] [blame] | 234 | |
| 235 | } // namespace android |