Moved analysis work from NBLog to PerformanceAnalysis
Test: dumpsys media.log
Change-Id: Ibb595a25d7484840933ee0076c469b2b4d55aeec
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
new file mode 100644
index 0000000..b3d5f1f
--- /dev/null
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define LOG_TAG "PerformanceAnalysis"
+// #define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <climits>
+#include <deque>
+#include <fstream>
+// #include <inttypes.h>
+#include <iostream>
+#include <math.h>
+#include <numeric>
+#include <vector>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <new>
+#include <audio_utils/roundup.h>
+#include <media/nbaio/NBLog.h>
+#include <media/nbaio/PerformanceAnalysis.h>
+// #include <utils/CallStack.h> // used to print callstack
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <queue>
+#include <utility>
+
+namespace android {
+
+PerformanceAnalysis::PerformanceAnalysis() : findGlitch(false) {
+ ALOGE("this value should be 4: %d", kPeriodMs);
+ kPeriodMsCPU = static_cast<int>(PerformanceAnalysis::kPeriodMs * kRatio);
+}
+
+static int widthOf(int x) {
+ int width = 0;
+ while (x > 0) {
+ ++width;
+ x /= 10;
+ }
+ return width;
+}
+
+// WIP: uploading this half-written function to get code review on
+// cleanup and new file creation.
+/*
+static std::vector<std::pair<int, int>> outlierIntervals(
+ const std::deque<std::pair<int, short_histogram>> &shortHists) {
+ // TODO: need the timestamps
+ if (shortHists.size() < 1) {
+ return;
+ }
+ // count number of outliers in histogram
+ // TODO: need the alertIfGlitch analysis on the time series in NBLog::reader
+ // to find all the glitches
+ const std::vector<int> glitchCount = std::vector<int>(shortHists.size());
+ // Total ms elapsed in each shortHist
+ const std::vector<int> timeElapsedMs = std::vector<int>(shortHists.size());
+ int i = 0;
+ for (const auto &shortHist: shortHists) {
+ for (const auto &bin: shortHist) {
+ timeElapsedMs.at(i) += bin->first * bin->second;
+ if (bin->first >= kGlitchThreshMs) {
+ glitchCount.at(i) += bin->second;
+ }
+ }
+ i++;
+ }
+ // seconds between glitches and corresponding timestamp
+ const std::vector<std::pair<double, int>> glitchFreeIntervalsSec;
+ // Sec since last glitch. nonzero if the duration spans many shortHists
+ double glitchFreeSec = 0;
+ for (int i = 0; i < kGlitchCount.size(); i++) {
+ if (kGlitchCount.at(i) == 0) {
+ glitchFreeSec += static_cast<double>timeElapsedMs.at(i) / kMsPerSec;
+ }
+ else {
+ // average time between glitches in this interval
+ const double kInterval = static_cast<double>(timeElapsedMs.at(i)) / kGlitchCount.at(i);
+ for (int j = 0; j < kGlitchCount.at(i); j++) {
+ kIntervals.emplace_front(kInterval);
+ }
+ }
+ }
+ return;
+}*/
+
+// TODO: implement peak detector
+/*
+static void peakDetector() {
+ return;
+} */
+
+// TODO put this function in separate file. Make it return a std::string instead of modifying body
+// TODO create a subclass of Reader for this and related work
+// FIXME: as can be seen when printing the values, the outlier timestamps typically occur
+// in the first histogram 35 to 38 indices from the end (most often 35).
+// TODO: build histogram buckets earlier and discard timestamps to save memory
+// TODO consider changing all ints to uint32_t or uint64_t
+void PerformanceAnalysis::reportPerformance(String8 *body,
+ const std::deque<std::pair
+ <int, short_histogram>> &shortHists,
+ int maxHeight) {
+ if (shortHists.size() < 1) {
+ return;
+ }
+ // this is temporary code, which only prints out one histogram
+ // of all data stored in buffer. The data is not erased, only overwritten.
+ // TODO: more elaborate data analysis
+ std::map<int, int> buckets;
+ for (const auto &shortHist: shortHists) {
+ for (const auto &countPair : shortHist.second) {
+ buckets[countPair.first] += countPair.second;
+ }
+ }
+
+ // underscores and spaces length corresponds to maximum width of histogram
+ static const int kLen = 40;
+ std::string underscores(kLen, '_');
+ std::string spaces(kLen, ' ');
+
+ auto it = buckets.begin();
+ int maxDelta = it->first;
+ int maxCount = it->second;
+ // Compute maximum values
+ while (++it != buckets.end()) {
+ if (it->first > maxDelta) {
+ maxDelta = it->first;
+ }
+ if (it->second > maxCount) {
+ maxCount = it->second;
+ }
+ }
+ int height = log2(maxCount) + 1; // maxCount > 0, safe to call log2
+ const int leftPadding = widthOf(1 << height);
+ const int colWidth = std::max(std::max(widthOf(maxDelta) + 1, 3), leftPadding + 2);
+ int scalingFactor = 1;
+ // scale data if it exceeds maximum height
+ if (height > maxHeight) {
+ scalingFactor = (height + maxHeight) / maxHeight;
+ height /= scalingFactor;
+ }
+ body->appendFormat("\n%*s", leftPadding + 11, "Occurrences");
+ // write histogram label line with bucket values
+ body->appendFormat("\n%s", " ");
+ body->appendFormat("%*s", leftPadding, " ");
+ for (auto const &x : buckets) {
+ body->appendFormat("%*d", colWidth, x.second);
+ }
+ // write histogram ascii art
+ body->appendFormat("\n%s", " ");
+ for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) {
+ const int value = 1 << row;
+ body->appendFormat("%.*s", leftPadding, spaces.c_str());
+ for (auto const &x : buckets) {
+ body->appendFormat("%.*s%s", colWidth - 1, spaces.c_str(), x.second < value ? " " : "|");
+ }
+ body->appendFormat("\n%s", " ");
+ }
+ // print x-axis
+ const int columns = static_cast<int>(buckets.size());
+ body->appendFormat("%*c", leftPadding, ' ');
+ body->appendFormat("%.*s", (columns + 1) * colWidth, underscores.c_str());
+ body->appendFormat("\n%s", " ");
+
+ // write footer with bucket labels
+ body->appendFormat("%*s", leftPadding, " ");
+ for (auto const &x : buckets) {
+ body->appendFormat("%*d", colWidth, x.first);
+ }
+ body->appendFormat("%.*s%s", colWidth, spaces.c_str(), "ms\n");
+
+}
+
+
+// Produces a log warning if the timing of recent buffer periods caused a glitch
+// Computes sum of running window of three buffer periods
+// Checks whether the buffer periods leave enough CPU time for the next one
+// e.g. if a buffer period is expected to be 4 ms and a buffer requires 3 ms of CPU time,
+// here are some glitch cases:
+// 4 + 4 + 6 ; 5 + 4 + 5; 2 + 2 + 10
+// TODO: develop this code to track changes in histogram distribution in addition
+// to / instead of glitches.
+void PerformanceAnalysis::alertIfGlitch(const std::vector<int64_t> &samples) {
+ std::deque<int> periods(kNumBuff, kPeriodMs);
+ for (size_t i = 2; i < samples.size(); ++i) { // skip first time entry
+ periods.push_front(deltaMs(samples[i - 1], samples[i]));
+ periods.pop_back();
+ // TODO: check that all glitch cases are covered
+ if (std::accumulate(periods.begin(), periods.end(), 0) > kNumBuff * kPeriodMs +
+ kPeriodMs - kPeriodMsCPU) {
+ ALOGW("A glitch occurred");
+ periods.assign(kNumBuff, kPeriodMs);
+ }
+ }
+ return;
+}
+
+bool PerformanceAnalysis::isFindGlitch() const
+{
+ return findGlitch;
+}
+
+void PerformanceAnalysis::setFindGlitch(bool s)
+{
+ findGlitch = s;
+}
+//TODO: ask Andy where to keep '= 4'
+const int PerformanceAnalysis::kPeriodMs; // = 4;
+
+} // namespace android