Merge "aaudio: fix getXRunCount am: faeb8b20e8 am: 74aa2e0d85" into oc-mr1-dev-plus-aosp
am: 18683df374
Change-Id: I8ae20ee66100231ba1daca20f982120a18e7471d
diff --git a/include/media/nbaio/ReportPerformance.h b/include/media/nbaio/ReportPerformance.h
new file mode 120000
index 0000000..bd596e3
--- /dev/null
+++ b/include/media/nbaio/ReportPerformance.h
@@ -0,0 +1 @@
+../../../media/libnbaio/include/media/nbaio/ReportPerformance.h
\ No newline at end of file
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index a5f9ab6..b1cb0e7 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -135,7 +135,11 @@
&mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
- ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
+ char typeBuffer[64], uuidBuffer[64];
+ guidToString(type, typeBuffer, sizeof(typeBuffer));
+ guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
+ ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
+ typeBuffer, uuidBuffer, mStatus);
if (iEffect == 0) {
mStatus = NO_INIT;
}
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index b976721..efe947a 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -649,8 +649,12 @@
status_t MediaPlayer::reset()
{
ALOGV("reset");
+ mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
- return reset_l();
+ status_t result = reset_l();
+ mLockThreadId = 0;
+
+ return result;
}
status_t MediaPlayer::setAudioStreamType(audio_stream_type_t type)
@@ -860,7 +864,7 @@
// this will deadlock.
//
// The threadId hack below works around this for the care of prepare,
- // seekTo and start within the same process.
+ // seekTo, start, and reset within the same process.
// FIXME: Remember, this is a hack, it's not even a hack that is applied
// consistently for all use-cases, this needs to be revisited.
if (mLockThreadId != getThreadId()) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index d83c406..c79b6f7 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -847,10 +847,6 @@
status_t NuPlayer::GenericSource::dequeueAccessUnit(
bool audio, sp<ABuffer> *accessUnit) {
- if (audio && !mStarted) {
- return -EWOULDBLOCK;
- }
-
// If has gone through stop/releaseDrm sequence, we no longer send down any buffer b/c
// the codec's crypto object has gone away (b/37960096).
// Note: This will be unnecessary when stop() changes behavior and releases codec (b/35248283).
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 2f639d2..08f0944 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -49,20 +49,25 @@
*
* 2) reading the data from shared memory
* Thread::threadloop()
-* TODO: add description?
* NBLog::MergeThread::threadLoop()
-* calls NBLog::Merger::merge
+* Waits on a mutex, called periodically
+* Calls NBLog::Merger::merge and MergeReader.getAndProcessSnapshot.
* NBLog::Merger::merge
* Merges snapshots sorted by timestamp
-* for each reader in vector of class NamedReader,
-* callsNamedReader::reader()->getSnapshot
-* TODO: check whether the rest of this function is relevant
+* Calls Reader::getSnapshot on each individual thread buffer to in shared
+* memory and writes all their data to the single FIFO stored in mMerger.
* NBLog::Reader::getSnapshot
* copies snapshot of reader's fifo buffer into its own buffer
* calls mFifoReader->obtain to find readable data
* sets snapshot.begin() and .end() iterators to boundaries of valid entries
* moves the fifo reader index to after the last entry read
* in this case, the buffer is in shared memory. in (4), the buffer is private
+* NBLog::MergeThread::getAndProcessSnapshot
+* Iterates through the entries in the local FIFO. Processes the data in
+* specific ways depending on the entry type. If the data is a histogram
+* timestamp or an audio on/off signal, writes to a map of PerformanceAnalysis
+* class instances, where the wakeup() intervals are stored as histograms
+* and analyzed.
*
* 3) reading the data from private buffer
* MediaLogService::dump
@@ -87,13 +92,11 @@
*/
#define LOG_TAG "NBLog"
-// #define LOG_NDEBUG 0
#include <algorithm>
#include <climits>
#include <deque>
#include <fstream>
-// #include <inttypes.h>
#include <iostream>
#include <math.h>
#include <numeric>
@@ -109,7 +112,7 @@
#include <media/nbaio/NBLog.h>
#include <media/nbaio/PerformanceAnalysis.h>
#include <media/nbaio/ReportPerformance.h>
-// #include <utils/CallStack.h> // used to print callstack
+#include <utils/CallStack.h>
#include <utils/Log.h>
#include <utils/String8.h>
@@ -766,8 +769,8 @@
NBLog::Event::EVENT_AUDIO_STATE};
NBLog::Reader::Reader(const void *shared, size_t size)
- : mShared((/*const*/ Shared *) shared), /*mIMemory*/
- mFd(-1), mIndent(0),
+ : mFd(-1), mIndent(0), mLost(0),
+ mShared((/*const*/ Shared *) shared), /*mIMemory*/
mFifo(mShared != NULL ?
new audio_utils_fifo(size, sizeof(uint8_t),
mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
@@ -805,6 +808,9 @@
return nullptr; // no entry found
}
+// Copies content of a Reader FIFO into its Snapshot
+// The Snapshot has the same raw data, but represented as a sequence of entries
+// and an EntryIterator making it possible to process the data.
std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
{
if (mFifoReader == NULL) {
@@ -870,23 +876,18 @@
}
-// TODO: move this to PerformanceAnalysis
-// TODO: make call to dump periodic so that data in shared FIFO does not get overwritten
-void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
+// Takes raw content of the local merger FIFO, processes log entries, and
+// writes the data to a map of class PerformanceAnalysis, based on their thread ID.
+void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot)
{
- mFd = fd;
- mIndent = indent;
String8 timestamp, body;
- // FIXME: this is not thread safe
- // TODO: need a separate instance of performanceAnalysis for each thread
- // used to store data and to call analysis functions
- static ReportPerformance::PerformanceAnalysis performanceAnalysis;
+ // TODO: check: is this thread safe?
+ // TODO: add lost data information and notification to ReportPerformance
size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
if (lost > 0) {
- body.appendFormat("warning: lost %zu bytes worth of events", lost);
- // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
- // log to push it out. Consider keeping the timestamp/body between calls to copyEntryDataAt().
- dumpLine(timestamp, body);
+ // TODO: ultimately, this will be += and reset to 0. TODO: check that this is
+ // obsolete now that Merger::merge is called periodically. No data should be lost
+ mLost = lost;
}
for (auto entry = snapshot.begin(); entry != snapshot.end();) {
@@ -902,12 +903,17 @@
memcpy(&hash, &(data->hash), sizeof(hash));
int64_t ts;
memcpy(&ts, &data->ts, sizeof(ts));
- performanceAnalysis.logTsEntry(ts);
+ mThreadPerformanceAnalysis[data->author][hash].logTsEntry(ts);
++entry;
break;
}
case EVENT_AUDIO_STATE: {
- performanceAnalysis.handleStateChange();
+ HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data);
+ // TODO This memcpies are here to avoid unaligned memory access crash.
+ // There's probably a more efficient way to do it
+ log_hash_t hash;
+ memcpy(&hash, &(data->hash), sizeof(hash));
+ mThreadPerformanceAnalysis[data->author][hash].handleStateChange();
++entry;
break;
}
@@ -922,19 +928,24 @@
break;
}
}
- performanceAnalysis.reportPerformance(&body);
+ // FIXME: decide whether to print the warnings here or elsewhere
if (!body.isEmpty()) {
dumpLine(timestamp, body);
}
}
-void NBLog::Reader::dump(int fd, size_t indent)
+void NBLog::MergeReader::getAndProcessSnapshot()
{
- // get a snapshot, dump it
+ // get a snapshot, process it
std::unique_ptr<Snapshot> snap = getSnapshot();
- dump(fd, indent, *snap);
+ getAndProcessSnapshot(*snap);
}
+void NBLog::MergeReader::dump(int fd, int indent) {
+ ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis);
+}
+
+// Writes a string to the console
void NBLog::Reader::dumpLine(const String8 ×tamp, String8 &body)
{
if (mFd >= 0) {
@@ -1093,6 +1104,7 @@
{}
void NBLog::Merger::addReader(const NBLog::NamedReader &reader) {
+
// FIXME This is called by binder thread in MediaLogService::registerWriter
// but the access to shared variable mNamedReaders is not yet protected by a lock.
mNamedReaders.push_back(reader);
@@ -1116,7 +1128,7 @@
return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index);
}
-// Merge registered readers, sorted by timestamp
+// Merge registered readers, sorted by timestamp, and write data to a single FIFO in local memory
void NBLog::Merger::merge() {
// FIXME This is called by merge thread
// but the access to shared variable mNamedReaders is not yet protected by a lock.
@@ -1173,8 +1185,9 @@
// ---------------------------------------------------------------------------
-NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
+NBLog::MergeThread::MergeThread(NBLog::Merger &merger, NBLog::MergeReader &mergeReader)
: mMerger(merger),
+ mMergeReader(mergeReader),
mTimeoutUs(0) {}
NBLog::MergeThread::~MergeThread() {
@@ -1196,7 +1209,12 @@
mTimeoutUs -= kThreadSleepPeriodUs;
}
if (doMerge) {
+ // Merge data from all the readers
mMerger.merge();
+ // Process the data collected by mMerger and write it to PerformanceAnalysis
+ // FIXME: decide whether to call getAndProcessSnapshot every time
+ // or whether to have a separate thread that calls it with a lower frequency
+ mMergeReader.getAndProcessSnapshot();
}
return true;
}
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
index fb3bddc..7f4548f 100644
--- a/media/libnbaio/PerformanceAnalysis.cpp
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -36,7 +36,6 @@
#include <media/nbaio/NBLog.h>
#include <media/nbaio/PerformanceAnalysis.h>
#include <media/nbaio/ReportPerformance.h>
-// #include <utils/CallStack.h> // used to print callstack
#include <utils/Log.h>
#include <utils/String8.h>
@@ -54,16 +53,6 @@
kPeriodMsCPU = static_cast<int>(kPeriodMs * kRatio);
}
-// converts a time series into a map. key: buffer period length. value: count
-static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
- // TODO allow buckets of variable resolution
- std::map<int, int> buckets;
- for (size_t i = 1; i < samples.size(); ++i) {
- ++buckets[deltaMs(samples[i - 1], samples[i])];
- }
- return buckets;
-}
-
static int widthOf(int x) {
int width = 0;
while (x > 0) {
@@ -79,21 +68,41 @@
// small or large values and stores these as peaks, and flushes
// the timestamp series from memory.
void PerformanceAnalysis::processAndFlushTimeStampSeries() {
+ if (mTimeStampSeries.empty()) {
+ ALOGD("Timestamp series is empty");
+ return;
+ }
+
+ // mHists is empty if thread/hash pair is sending data for the first time
+ if (mHists.empty()) {
+ mHists.emplace_front(static_cast<uint64_t>(mTimeStampSeries[0]),
+ std::map<int, int>());
+ }
+
// 1) analyze the series to store all outliers and their exact timestamps:
storeOutlierData(mTimeStampSeries);
// 2) detect peaks in the outlier series
detectPeaks();
- // 3) compute its histogram, append to mRecentHists and clear the time series
- mRecentHists.emplace_back(static_cast<timestamp>(mTimeStampSeries[0]),
- buildBuckets(mTimeStampSeries));
- // do not let mRecentHists exceed capacity
- // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
- if (mRecentHists.size() >= kRecentHistsCapacity) {
- // ALOGD("popped back mRecentHists");
- mRecentHists.pop_front();
+ // if the current histogram has spanned its maximum time interval,
+ // insert a new empty histogram to the front of mHists
+ if (deltaMs(mHists[0].first, mTimeStampSeries[0]) >= kMaxLength.HistTimespanMs) {
+ mHists.emplace_front(static_cast<uint64_t>(mTimeStampSeries[0]),
+ std::map<int, int>());
+ // When memory is full, delete oldest histogram
+ if (mHists.size() >= kMaxLength.Hists) {
+ mHists.resize(kMaxLength.Hists);
+ }
}
+
+ // 3) add current time intervals to histogram
+ for (size_t i = 1; i < mTimeStampSeries.size(); ++i) {
+ ++mHists[0].second[deltaMs(
+ mTimeStampSeries[i - 1], mTimeStampSeries[i])];
+ }
+
+ // clear the timestamps
mTimeStampSeries.clear();
}
@@ -116,49 +125,11 @@
mTimeStampSeries.push_back(ts);
// if length of the time series has reached kShortHistSize samples,
// analyze the data and flush the timestamp series from memory
- if (mTimeStampSeries.size() >= kShortHistSize) {
+ if (mTimeStampSeries.size() >= kMaxLength.TimeStamps) {
processAndFlushTimeStampSeries();
}
}
-// When the short-term histogram array mRecentHists has reached capacity,
-// merge histograms for data compression and store them in mLongTermHists
-// clears mRecentHists
-// TODO: have logTsEntry write directly to mLongTermHists, discard mRecentHists,
-// start a new histogram when a peak occurs
-void PerformanceAnalysis::processAndFlushRecentHists() {
-
- // Buckets is used to aggregate short-term histograms.
- Histogram buckets;
- timestamp startingTs = mRecentHists[0].first;
-
- for (const auto &shortHist: mRecentHists) {
- // If the time between starting and ending timestamps has reached the maximum,
- // add the current histogram (buckets) to the long-term histogram buffer,
- // clear buckets, and start a new long-term histogram aggregation process.
- if (deltaMs(startingTs, shortHist.first) >= kMaxHistTimespanMs) {
- mLongTermHists.emplace_back(startingTs, std::move(buckets));
- buckets.clear();
- startingTs = shortHist.first;
- // When memory is full, delete oldest histogram
- // TODO use a circular buffer
- if (mLongTermHists.size() >= kLongTermHistsCapacity) {
- mLongTermHists.pop_front();
- }
- }
-
- // add current histogram to buckets
- for (const auto &countPair : shortHist.second) {
- buckets[countPair.first] += countPair.second;
- }
- }
- mRecentHists.clear();
- // TODO: decide when/where to call writeToFile
- // TODO: add a thread-specific extension to the file name
- static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt";
- writeToFile(mOutlierData, mLongTermHists, kName, false);
-}
-
// Given a series of outlier intervals (mOutlier data),
// looks for changes in distribution (peaks), which can be either positive or negative.
// The function sets the mean to the starting value and sigma to 0, and updates
@@ -181,36 +152,39 @@
// the mean and standard deviation are updated every time a peak is detected
// initialize first time. The mean from the previous sequence is stored
// for the next sequence. Here, they are initialized for the first time.
- if (mPeakDetectorMean < 0) {
- mPeakDetectorMean = static_cast<double>(start->first);
- mPeakDetectorSd = 0;
+ if (mOutlierDistribution.Mean < 0) {
+ mOutlierDistribution.Mean = static_cast<double>(start->first);
+ mOutlierDistribution.Sd = 0;
}
auto sqr = [](auto x){ return x * x; };
for (auto it = mOutlierData.begin(); it != mOutlierData.end(); ++it) {
// no surprise occurred:
// the new element is a small number of standard deviations from the mean
- if ((fabs(it->first - mPeakDetectorMean) < kStddevThreshold * mPeakDetectorSd) ||
+ if ((fabs(it->first - mOutlierDistribution.Mean) <
+ mOutlierDistribution.kMaxDeviation * mOutlierDistribution.Sd) ||
// or: right after peak has been detected, the delta is smaller than average
- (mPeakDetectorSd == 0 && fabs(it->first - mPeakDetectorMean) < kTypicalDiff)) {
+ (mOutlierDistribution.Sd == 0 &&
+ fabs(it->first - mOutlierDistribution.Mean) < kTypicalDiff)) {
// update the mean and sd:
// count number of elements (distance between start interator and current)
const int kN = std::distance(start, it) + 1;
// usual formulas for mean and sd
- mPeakDetectorMean = std::accumulate(start, it + 1, 0.0,
+ mOutlierDistribution.Mean = std::accumulate(start, it + 1, 0.0,
[](auto &a, auto &b){return a + b.first;}) / kN;
- mPeakDetectorSd = sqrt(std::accumulate(start, it + 1, 0.0,
- [=](auto &a, auto &b){ return a + sqr(b.first - mPeakDetectorMean);})) /
- ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance
+ mOutlierDistribution.Sd = sqrt(std::accumulate(start, it + 1, 0.0,
+ [=](auto &a, auto &b){
+ return a + sqr(b.first - mOutlierDistribution.Mean);})) /
+ ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance
}
// surprising value: store peak timestamp and reset mean, sd, and start iterator
else {
- mPeakTimestamps.emplace_back(it->second);
- // TODO: remove pop_front once a circular buffer is in place
- if (mPeakTimestamps.size() >= kPeakSeriesSize) {
- mPeakTimestamps.pop_front();
+ mPeakTimestamps.emplace_front(it->second);
+ // TODO: turn this into a circular buffer
+ if (mPeakTimestamps.size() >= kMaxLength.Peaks) {
+ mPeakTimestamps.resize(kMaxLength.Peaks);
}
- mPeakDetectorMean = static_cast<double>(it->first);
- mPeakDetectorSd = 0;
+ mOutlierDistribution.Mean = static_cast<double>(it->first);
+ mOutlierDistribution.Sd = 0;
start = it;
}
}
@@ -219,7 +193,8 @@
// Called by LogTsEntry. The input is a vector of timestamps.
// Finds outliers and writes to mOutlierdata.
-// Each value in mOutlierdata consists of: <outlier timestamp, time elapsed since previous outlier>.
+// Each value in mOutlierdata consists of: <outlier timestamp,
+// time elapsed since previous outlier>.
// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
// This function is applied to the time series before it is converted into a histogram.
void PerformanceAnalysis::storeOutlierData(const std::vector<int64_t> ×tamps) {
@@ -227,54 +202,43 @@
return;
}
// first pass: need to initialize
- if (mElapsed == 0) {
- mPrevNs = timestamps[0];
+ if (mOutlierDistribution.Elapsed == 0) {
+ mOutlierDistribution.PrevNs = timestamps[0];
}
for (const auto &ts: timestamps) {
- const uint64_t diffMs = static_cast<uint64_t>(deltaMs(mPrevNs, ts));
+ const uint64_t diffMs = static_cast<uint64_t>(deltaMs(mOutlierDistribution.PrevNs, ts));
if (diffMs >= static_cast<uint64_t>(kOutlierMs)) {
- mOutlierData.emplace_back(mElapsed, static_cast<uint64_t>(mPrevNs));
+ mOutlierData.emplace_front(mOutlierDistribution.Elapsed,
+ static_cast<uint64_t>(mOutlierDistribution.PrevNs));
// Remove oldest value if the vector is full
// TODO: remove pop_front once circular buffer is in place
// FIXME: make sure kShortHistSize is large enough that that data will never be lost
// before being written to file or to a FIFO
- if (mOutlierData.size() >= kOutlierSeriesSize) {
- mOutlierData.pop_front();
+ if (mOutlierData.size() >= kMaxLength.Outliers) {
+ mOutlierData.resize(kMaxLength.Outliers);
}
- mElapsed = 0;
+ mOutlierDistribution.Elapsed = 0;
}
- mElapsed += diffMs;
- mPrevNs = ts;
+ mOutlierDistribution.Elapsed += diffMs;
+ mOutlierDistribution.PrevNs = ts;
}
}
-
-// FIXME: delete this temporary test code, recycled for various new functions
-void PerformanceAnalysis::testFunction() {
- // produces values (4: 5000000), (13: 18000000)
- // ns timestamps of buffer periods
- const std::vector<int64_t>kTempTestData = {1000000, 4000000, 5000000,
- 16000000, 18000000, 28000000};
- PerformanceAnalysis::storeOutlierData(kTempTestData);
- for (const auto &outlier: mOutlierData) {
- ALOGE("PerformanceAnalysis test %lld: %lld",
- static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
- }
- detectPeaks();
-}
-
// TODO Make it return a std::string instead of modifying body --> is this still relevant?
// TODO consider changing all ints to uint32_t or uint64_t
// TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis
void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
- if (mRecentHists.size() < 1) {
- ALOGD("reportPerformance: mRecentHists is empty");
+ // Add any new data
+ processAndFlushTimeStampSeries();
+
+ if (mHists.empty()) {
+ ALOGD("reportPerformance: mHists is empty");
return;
}
- ALOGD("reportPerformance: hists size %d", static_cast<int>(mRecentHists.size()));
+ ALOGD("reportPerformance: hists size %d", static_cast<int>(mHists.size()));
// TODO: more elaborate data analysis
std::map<int, int> buckets;
- for (const auto &shortHist: mRecentHists) {
+ for (const auto &shortHist: mHists) {
for (const auto &countPair : shortHist.second) {
buckets[countPair.first] += countPair.second;
}
@@ -306,6 +270,7 @@
scalingFactor = (height + maxHeight) / maxHeight;
height /= scalingFactor;
}
+ // TODO: print reader (author) ID
body->appendFormat("\n%*s", leftPadding + 11, "Occurrences");
// write histogram label line with bucket values
body->appendFormat("\n%s", " ");
@@ -319,7 +284,8 @@
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("%.*s%s", colWidth - 1,
+ spaces.c_str(), x.second < value ? " " : "|");
}
body->appendFormat("\n%s", " ");
}
@@ -345,15 +311,15 @@
}
-
+// TODO: decide whether to use this or whether it is overkill, and it is enough
+// to only treat as glitches single wakeup call intervals which are too long.
+// Ultimately, glitch detection will be directly on the audio signal.
// 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
@@ -369,6 +335,34 @@
return;
}
+//------------------------------------------------------------------------------
+
+// writes summary of performance into specified file descriptor
+void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) {
+ String8 body;
+ const char* const kDirectory = "/data/misc/audioserver/";
+ for (auto & thread : threadPerformanceAnalysis) {
+ for (auto & hash: thread.second) {
+ PerformanceAnalysis& curr = hash.second;
+ curr.processAndFlushTimeStampSeries();
+ // write performance data to console
+ curr.reportPerformance(&body);
+ // write to file
+ writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps,
+ kDirectory, false, thread.first, hash.first);
+ }
+ }
+ if (!body.isEmpty()) {
+ dumpLine(fd, indent, body);
+ body.clear();
+ }
+}
+
+// Writes a string into specified file descriptor
+void dumpLine(int fd, int indent, const String8 &body) {
+ dprintf(fd, "%.*s%s \n", indent, "", body.string());
+}
+
} // namespace ReportPerformance
} // namespace android
diff --git a/media/libnbaio/ReportPerformance.cpp b/media/libnbaio/ReportPerformance.cpp
index dc50ada..fa2b9a0 100644
--- a/media/libnbaio/ReportPerformance.cpp
+++ b/media/libnbaio/ReportPerformance.cpp
@@ -23,12 +23,12 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
+#include <sstream>
#include <sys/prctl.h>
#include <utility>
#include <media/nbaio/NBLog.h>
#include <media/nbaio/PerformanceAnalysis.h>
#include <media/nbaio/ReportPerformance.h>
-// #include <utils/CallStack.h> // used to print callstack
#include <utils/Log.h>
#include <utils/String8.h>
@@ -38,35 +38,64 @@
// Writes outlier intervals, timestamps, and histograms spanning long time intervals to a file.
// TODO: format the data efficiently and write different types of data to different files
-void writeToFile(std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
- std::deque<std::pair<timestamp, Histogram>> &hists,
- const char * kName,
- bool append) {
- ALOGD("writing performance data to file");
+void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+ const std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
+ const std::deque<timestamp> &peakTimestamps,
+ const char * directory, bool append, int author, log_hash_t hash) {
if (outlierData.empty() || hists.empty()) {
+ ALOGW("No data, returning.");
return;
}
+ std::stringstream outlierName;
+ std::stringstream histogramName;
+ std::stringstream peakName;
+
+ histogramName << directory << "histograms_" << author << "_" << hash;
+ outlierName << directory << "outliers_" << author << "_" << hash;
+ peakName << directory << "peaks_" << author << "_" << hash;
+
+ std::ofstream hfs;
+ hfs.open(histogramName.str(), append ? std::ios::app : std::ios::trunc);
+ if (!hfs.is_open()) {
+ ALOGW("couldn't open file %s", histogramName.str().c_str());
+ return;
+ }
+ hfs << "Histogram data\n";
+ for (const auto &hist : hists) {
+ hfs << "\ttimestamp\n";
+ hfs << hist.first << "\n";
+ hfs << "\tbuckets and counts\n";
+ for (const auto &bucket : hist.second) {
+ hfs << bucket.first << ": " << bucket.second << "\n";
+ }
+ hfs << "\n"; // separate histograms with a newline
+ }
+ hfs.close();
+
std::ofstream ofs;
- ofs.open(kName, append ? std::ios::app : std::ios::trunc);
+ ofs.open(outlierName.str(), append ? std::ios::app : std::ios::trunc);
if (!ofs.is_open()) {
- ALOGW("couldn't open file %s", kName);
+ ALOGW("couldn't open file %s", outlierName.str().c_str());
return;
}
ofs << "Outlier data: interval and timestamp\n";
for (const auto &outlier : outlierData) {
ofs << outlier.first << ": " << outlier.second << "\n";
}
- ofs << "Histogram data\n";
- for (const auto &hist : hists) {
- ofs << "\ttimestamp\n";
- ofs << hist.first << "\n";
- ofs << "\tbuckets and counts\n";
- for (const auto &bucket : hist.second) {
- ofs << bucket.first << ": " << bucket.second << "\n";
- }
- }
ofs.close();
+
+ std::ofstream pfs;
+ pfs.open(peakName.str(), append ? std::ios::app : std::ios::trunc);
+ if (!pfs.is_open()) {
+ ALOGW("couldn't open file %s", peakName.str().c_str());
+ return;
+ }
+ pfs << "Peak data: timestamp\n";
+ for (const auto &peak : peakTimestamps) {
+ pfs << peak << "\n";
+ }
+ pfs.close();
}
} // namespace ReportPerformance
diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h
index 3e48ee1..bd17674 100644
--- a/media/libnbaio/include/media/nbaio/NBLog.h
+++ b/media/libnbaio/include/media/nbaio/NBLog.h
@@ -19,16 +19,18 @@
#ifndef ANDROID_MEDIA_NBLOG_H
#define ANDROID_MEDIA_NBLOG_H
-#include <binder/IMemory.h>
-#include <audio_utils/fifo.h>
-#include <utils/Mutex.h>
-#include <utils/threads.h>
-
-#include <map>
#include <deque>
+#include <map>
#include <set>
#include <vector>
+#include <audio_utils/fifo.h>
+#include <binder/IMemory.h>
+#include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
namespace android {
class String8;
@@ -37,234 +39,236 @@
public:
-typedef uint64_t log_hash_t;
+ typedef uint64_t log_hash_t;
-// FIXME Everything needed for client (writer API and registration) should be isolated
-// from the rest of the implementation.
-class Writer;
-class Reader;
+ // FIXME Everything needed for client (writer API and registration) should be isolated
+ // from the rest of the implementation.
+ class Writer;
+ class Reader;
-enum Event : uint8_t {
- EVENT_RESERVED,
- EVENT_STRING, // ASCII string, not NUL-terminated
- // TODO: make timestamp optional
- EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC)
- EVENT_INTEGER, // integer value entry
- EVENT_FLOAT, // floating point value entry
- EVENT_PID, // process ID and process name
- EVENT_AUTHOR, // author index (present in merged logs) tracks entry's original log
- EVENT_START_FMT, // logFormat start event: entry includes format string, following
- // entries contain format arguments
- EVENT_HASH, // unique HASH of log origin, originates from hash of file name
- // and line number
- EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram
- EVENT_AUDIO_STATE, // audio on/off event: logged upon FastMixer::onStateChange() call
- EVENT_END_FMT, // end of logFormat argument list
+ enum Event : uint8_t {
+ EVENT_RESERVED,
+ EVENT_STRING, // ASCII string, not NUL-terminated
+ // TODO: make timestamp optional
+ EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC)
+ EVENT_INTEGER, // integer value entry
+ EVENT_FLOAT, // floating point value entry
+ EVENT_PID, // process ID and process name
+ EVENT_AUTHOR, // author index (present in merged logs) tracks entry's
+ // original log
+ EVENT_START_FMT, // logFormat start event: entry includes format string,
+ // following entries contain format arguments
+ EVENT_HASH, // unique HASH of log origin, originates from hash of file name
+ // and line number
+ EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram
+ EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call
+ EVENT_END_FMT, // end of logFormat argument list
- EVENT_UPPER_BOUND, // to check for invalid events
-};
+ EVENT_UPPER_BOUND, // to check for invalid events
+ };
private:
-// ---------------------------------------------------------------------------
-// API for handling format entry operations
+ // ---------------------------------------------------------------------------
+ // API for handling format entry operations
-// a formatted entry has the following structure:
-// * START_FMT entry, containing the format string
-// * TIMESTAMP entry
-// * HASH entry
-// * author entry of the thread that generated it (optional, present in merged log)
-// * format arg1
-// * format arg2
-// * ...
-// * END_FMT entry
+ // a formatted entry has the following structure:
+ // * START_FMT entry, containing the format string
+ // * TIMESTAMP entry
+ // * HASH entry
+ // * author entry of the thread that generated it (optional, present in merged log)
+ // * format arg1
+ // * format arg2
+ // * ...
+ // * END_FMT entry
-// entry representation in memory
-struct entry {
- const uint8_t type;
- const uint8_t length;
- const uint8_t data[0];
-};
+ // entry representation in memory
+ struct entry {
+ const uint8_t type;
+ const uint8_t length;
+ const uint8_t data[0];
+ };
-// entry tail representation (after data)
-struct ending {
- uint8_t length;
- uint8_t next[0];
-};
+ // entry tail representation (after data)
+ struct ending {
+ uint8_t length;
+ uint8_t next[0];
+ };
-// entry iterator
-class EntryIterator {
-public:
- EntryIterator();
- explicit EntryIterator(const uint8_t *entry);
- EntryIterator(const EntryIterator &other);
+ // entry iterator
+ class EntryIterator {
+ public:
+ EntryIterator();
+ explicit EntryIterator(const uint8_t *entry);
+ EntryIterator(const EntryIterator &other);
- // dereference underlying entry
- const entry& operator*() const;
- const entry* operator->() const;
- // advance to next entry
- EntryIterator& operator++(); // ++i
- // back to previous entry
- EntryIterator& operator--(); // --i
- EntryIterator next() const;
- EntryIterator prev() const;
- bool operator!=(const EntryIterator &other) const;
- int operator-(const EntryIterator &other) const;
+ // dereference underlying entry
+ const entry& operator*() const;
+ const entry* operator->() const;
+ // advance to next entry
+ EntryIterator& operator++(); // ++i
+ // back to previous entry
+ EntryIterator& operator--(); // --i
+ EntryIterator next() const;
+ EntryIterator prev() const;
+ bool operator!=(const EntryIterator &other) const;
+ int operator-(const EntryIterator &other) const;
- bool hasConsistentLength() const;
- void copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
- void copyData(uint8_t *dst) const;
+ bool hasConsistentLength() const;
+ void copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
+ void copyData(uint8_t *dst) const;
- template<typename T>
- inline const T& payload() {
- return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
- }
+ template<typename T>
+ inline const T& payload() {
+ return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
+ }
- inline operator const uint8_t*() const {
- return ptr;
- }
+ inline operator const uint8_t*() const {
+ return ptr;
+ }
-private:
- const uint8_t *ptr;
-};
+ private:
+ const uint8_t *ptr;
+ };
-class AbstractEntry {
-public:
+ class AbstractEntry {
+ public:
- // Entry starting in the given pointer
- explicit AbstractEntry(const uint8_t *entry);
- virtual ~AbstractEntry() {}
+ // Entry starting in the given pointer
+ explicit AbstractEntry(const uint8_t *entry);
+ virtual ~AbstractEntry() {}
- // build concrete entry of appropriate class from pointer
- static std::unique_ptr<AbstractEntry> buildEntry(const uint8_t *ptr);
+ // build concrete entry of appropriate class from pointer
+ static std::unique_ptr<AbstractEntry> buildEntry(const uint8_t *ptr);
- // get format entry timestamp
- // TODO consider changing to uint64_t
- virtual int64_t timestamp() const = 0;
+ // get format entry timestamp
+ // TODO consider changing to uint64_t
+ virtual int64_t timestamp() const = 0;
- // get format entry's unique id
- virtual log_hash_t hash() const = 0;
+ // get format entry's unique id
+ virtual log_hash_t hash() const = 0;
- // entry's author index (-1 if none present)
- // a Merger has a vector of Readers, author simply points to the index of the
- // Reader that originated the entry
- // TODO consider changing to uint32_t
- virtual int author() const = 0;
+ // entry's author index (-1 if none present)
+ // a Merger has a vector of Readers, author simply points to the index of the
+ // Reader that originated the entry
+ // TODO consider changing to uint32_t
+ virtual int author() const = 0;
- // copy entry, adding author before timestamp, returns iterator to end of entry
- virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
- int author) const = 0;
+ // copy entry, adding author before timestamp, returns iterator to end of entry
+ virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+ int author) const = 0;
-protected:
- // copies ordinary entry from src to dst, and returns length of entry
- // size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it);
- const uint8_t *mEntry;
-};
+ protected:
+ // copies ordinary entry from src to dst, and returns length of entry
+ // size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it);
+ const uint8_t *mEntry;
+ };
-class FormatEntry : public AbstractEntry {
-public:
- // explicit FormatEntry(const EntryIterator &it);
- explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {}
- virtual ~FormatEntry() {}
+ class FormatEntry : public AbstractEntry {
+ public:
+ // explicit FormatEntry(const EntryIterator &it);
+ explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {}
+ virtual ~FormatEntry() {}
- EntryIterator begin() const;
+ EntryIterator begin() const;
- // Entry's format string
- const char* formatString() const;
+ // Entry's format string
+ const char* formatString() const;
- // Enrty's format string length
- size_t formatStringLength() const;
+ // Enrty's format string length
+ size_t formatStringLength() const;
- // Format arguments (excluding format string, timestamp and author)
- EntryIterator args() const;
+ // Format arguments (excluding format string, timestamp and author)
+ EntryIterator args() const;
- // get format entry timestamp
- virtual int64_t timestamp() const override;
+ // get format entry timestamp
+ virtual int64_t timestamp() const override;
- // get format entry's unique id
- virtual log_hash_t hash() const override;
+ // get format entry's unique id
+ virtual log_hash_t hash() const override;
- // entry's author index (-1 if none present)
- // a Merger has a vector of Readers, author simply points to the index of the
- // Reader that originated the entry
- virtual int author() const override;
+ // entry's author index (-1 if none present)
+ // a Merger has a vector of Readers, author simply points to the index of the
+ // Reader that originated the entry
+ virtual int author() const override;
- // copy entry, adding author before timestamp, returns size of original entry
- virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
- int author) const override;
+ // copy entry, adding author before timestamp, returns size of original entry
+ virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+ int author) const override;
-};
+ };
-class HistogramEntry : public AbstractEntry {
-public:
- explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) {
- }
- virtual ~HistogramEntry() {}
+ class HistogramEntry : public AbstractEntry {
+ public:
+ explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) {
+ }
+ virtual ~HistogramEntry() {}
- virtual int64_t timestamp() const override;
+ virtual int64_t timestamp() const override;
- virtual log_hash_t hash() const override;
+ virtual log_hash_t hash() const override;
- virtual int author() const override;
+ virtual int author() const override;
- virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
- int author) const override;
+ virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
+ int author) const override;
-};
+ };
-// ---------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------
-// representation of a single log entry in private memory
-struct Entry {
- Entry(Event event, const void *data, size_t length)
- : mEvent(event), mLength(length), mData(data) { }
- /*virtual*/ ~Entry() { }
+ // representation of a single log entry in private memory
+ struct Entry {
+ Entry(Event event, const void *data, size_t length)
+ : mEvent(event), mLength(length), mData(data) { }
+ /*virtual*/ ~Entry() { }
- // used during writing to format Entry information as follows: [type][length][data ... ][length]
- int copyEntryDataAt(size_t offset) const;
+ // used during writing to format Entry information as follows:
+ // [type][length][data ... ][length]
+ int copyEntryDataAt(size_t offset) const;
-private:
- friend class Writer;
- Event mEvent; // event type
- uint8_t mLength; // length of additional data, 0 <= mLength <= kMaxLength
- const void *mData; // event type-specific data
- static const size_t kMaxLength = 255;
-public:
- // mEvent, mLength, mData[...], duplicate mLength
- static const size_t kOverhead = sizeof(entry) + sizeof(ending);
- // endind length of previous entry
- static const size_t kPreviousLengthOffset = - sizeof(ending) +
- offsetof(ending, length);
-};
+ private:
+ friend class Writer;
+ Event mEvent; // event type
+ uint8_t mLength; // length of additional data, 0 <= mLength <= kMaxLength
+ const void *mData; // event type-specific data
+ static const size_t kMaxLength = 255;
+ public:
+ // mEvent, mLength, mData[...], duplicate mLength
+ static const size_t kOverhead = sizeof(entry) + sizeof(ending);
+ // endind length of previous entry
+ static const size_t kPreviousLengthOffset = - sizeof(ending) +
+ offsetof(ending, length);
+ };
-struct HistTsEntry {
- log_hash_t hash;
- int64_t ts;
-}; //TODO __attribute__((packed));
+ struct HistTsEntry {
+ log_hash_t hash;
+ int64_t ts;
+ }; //TODO __attribute__((packed));
-struct HistTsEntryWithAuthor {
- log_hash_t hash;
- int64_t ts;
- int author;
-}; //TODO __attribute__((packed));
+ struct HistTsEntryWithAuthor {
+ log_hash_t hash;
+ int64_t ts;
+ int author;
+ }; //TODO __attribute__((packed));
-using StateTsEntryWithAuthor = HistTsEntryWithAuthor;
+ using StateTsEntryWithAuthor = HistTsEntryWithAuthor;
-struct HistIntEntry {
- log_hash_t hash;
- int value;
-}; //TODO __attribute__((packed));
+ struct HistIntEntry {
+ log_hash_t hash;
+ int value;
+ }; //TODO __attribute__((packed));
-// representation of a single log entry in shared memory
-// byte[0] mEvent
-// byte[1] mLength
-// byte[2] mData[0]
-// ...
-// byte[2+i] mData[i]
-// ...
-// byte[2+mLength-1] mData[mLength-1]
-// byte[2+mLength] duplicate copy of mLength to permit reverse scan
-// byte[3+mLength] start of next log entry
+ // representation of a single log entry in shared memory
+ // byte[0] mEvent
+ // byte[1] mLength
+ // byte[2] mData[0]
+ // ...
+ // byte[2+i] mData[i]
+ // ...
+ // byte[2+mLength-1] mData[mLength-1]
+ // byte[2+mLength] duplicate copy of mLength to permit reverse scan
+ // byte[3+mLength] start of next log entry
static void appendInt(String8 *body, const void *data);
static void appendFloat(String8 *body, const void *data);
@@ -275,309 +279,332 @@
static String8 bufferDump(const EntryIterator &it);
public:
-// Located in shared memory, must be POD.
-// Exactly one process must explicitly call the constructor or use placement new.
-// Since this is a POD, the destructor is empty and unnecessary to call it explicitly.
-struct Shared {
- Shared() /* mRear initialized via default constructor */ { }
- /*virtual*/ ~Shared() { }
+ // Located in shared memory, must be POD.
+ // Exactly one process must explicitly call the constructor or use placement new.
+ // Since this is a POD, the destructor is empty and unnecessary to call it explicitly.
+ struct Shared {
+ Shared() /* mRear initialized via default constructor */ { }
+ /*virtual*/ ~Shared() { }
- audio_utils_fifo_index mRear; // index one byte past the end of most recent Entry
- char mBuffer[0]; // circular buffer for entries
-};
-
-public:
-
-// ---------------------------------------------------------------------------
-
-// FIXME Timeline was intended to wrap Writer and Reader, but isn't actually used yet.
-// For now it is just a namespace for sharedSize().
-class Timeline : public RefBase {
-public:
-#if 0
- Timeline(size_t size, void *shared = NULL);
- virtual ~Timeline();
-#endif
-
- // Input parameter 'size' is the desired size of the timeline in byte units.
- // Returns the size rounded up to a power-of-2, plus the constant size overhead for indices.
- static size_t sharedSize(size_t size);
-
-#if 0
-private:
- friend class Writer;
- friend class Reader;
-
- const size_t mSize; // circular buffer size in bytes, must be a power of 2
- bool mOwn; // whether I own the memory at mShared
- Shared* const mShared; // pointer to shared memory
-#endif
-};
-
-// ---------------------------------------------------------------------------
-
-// Writer is thread-safe with respect to Reader, but not with respect to multiple threads
-// calling Writer methods. If you need multi-thread safety for writing, use LockedWriter.
-class Writer : public RefBase {
-public:
- Writer(); // dummy nop implementation without shared memory
-
- // Input parameter 'size' is the desired size of the timeline in byte units.
- // The size of the shared memory must be at least Timeline::sharedSize(size).
- Writer(void *shared, size_t size);
- Writer(const sp<IMemory>& iMemory, size_t size);
-
- virtual ~Writer();
-
- // FIXME needs comments, and some should be private
- virtual void log(const char *string);
- virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
- virtual void logvf(const char *fmt, va_list ap);
- virtual void logTimestamp();
- virtual void logTimestamp(const int64_t ts);
- virtual void logInteger(const int x);
- virtual void logFloat(const float x);
- virtual void logPID();
- virtual void logFormat(const char *fmt, log_hash_t hash, ...);
- virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap);
- virtual void logStart(const char *fmt);
- virtual void logEnd();
- virtual void logHash(log_hash_t hash);
- virtual void logEventHistTs(Event event, log_hash_t hash);
-
- virtual bool isEnabled() const;
-
- // return value for all of these is the previous isEnabled()
- virtual bool setEnabled(bool enabled); // but won't enable if no shared memory
- bool enable() { return setEnabled(true); }
- bool disable() { return setEnabled(false); }
-
- sp<IMemory> getIMemory() const { return mIMemory; }
-
-private:
- // 0 <= length <= kMaxLength
- // writes a single Entry to the FIFO
- void log(Event event, const void *data, size_t length);
- // checks validity of an event before calling log above this one
- void log(const Entry *entry, bool trusted = false);
-
- Shared* const mShared; // raw pointer to shared memory
- sp<IMemory> mIMemory; // ref-counted version, initialized in constructor and then const
- audio_utils_fifo * const mFifo; // FIFO itself,
- // non-NULL unless constructor fails
- audio_utils_fifo_writer * const mFifoWriter; // used to write to FIFO,
- // non-NULL unless dummy constructor used
- bool mEnabled; // whether to actually log
-
- // cached pid and process name to use in %p format specifier
- // total tag length is mPidTagSize and process name is not zero terminated
- char *mPidTag;
- size_t mPidTagSize;
-};
-
-// ---------------------------------------------------------------------------
-
-// Similar to Writer, but safe for multiple threads to call concurrently
-class LockedWriter : public Writer {
-public:
- LockedWriter();
- LockedWriter(void *shared, size_t size);
-
- virtual void log(const char *string);
- virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
- virtual void logvf(const char *fmt, va_list ap);
- virtual void logTimestamp();
- virtual void logTimestamp(const int64_t ts);
- virtual void logInteger(const int x);
- virtual void logFloat(const float x);
- virtual void logPID();
- virtual void logStart(const char *fmt);
- virtual void logEnd();
- virtual void logHash(log_hash_t hash);
-
- virtual bool isEnabled() const;
- virtual bool setEnabled(bool enabled);
-
-private:
- mutable Mutex mLock;
-};
-
-// ---------------------------------------------------------------------------
-
-class Reader : public RefBase {
-public:
-
- // A snapshot of a readers buffer
- // This is raw data. No analysis has been done on it
- class Snapshot {
- public:
- Snapshot() : mData(NULL), mLost(0) {}
-
- Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
-
- ~Snapshot() { delete[] mData; }
-
- // copy of the buffer
- uint8_t *data() const { return mData; }
-
- // amount of data lost (given by audio_utils_fifo_reader)
- size_t lost() const { return mLost; }
-
- // iterator to beginning of readable segment of snapshot
- // data between begin and end has valid entries
- EntryIterator begin() { return mBegin; }
-
- // iterator to end of readable segment of snapshot
- EntryIterator end() { return mEnd; }
-
- private:
- friend class Reader;
- uint8_t *mData;
- size_t mLost;
- EntryIterator mBegin;
- EntryIterator mEnd;
+ audio_utils_fifo_index mRear; // index one byte past the end of most recent Entry
+ char mBuffer[0]; // circular buffer for entries
};
- // Input parameter 'size' is the desired size of the timeline in byte units.
- // The size of the shared memory must be at least Timeline::sharedSize(size).
- Reader(const void *shared, size_t size);
- Reader(const sp<IMemory>& iMemory, size_t size);
-
- virtual ~Reader();
-
- // get snapshot of readers fifo buffer, effectively consuming the buffer
- std::unique_ptr<Snapshot> getSnapshot();
- // dump a particular snapshot of the reader
- // TODO: move dump to PerformanceAnalysis. Model/view/controller design
- void dump(int fd, size_t indent, Snapshot & snap);
- // dump the current content of the reader's buffer (call getSnapshot() and previous dump())
- void dump(int fd, size_t indent = 0);
- bool isIMemory(const sp<IMemory>& iMemory) const;
-
-private:
-
- static const std::set<Event> startingTypes;
- static const std::set<Event> endingTypes;
- /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not
- // declared as const because audio_utils_fifo() constructor
- sp<IMemory> mIMemory; // ref-counted version, assigned only in constructor
- int mFd; // file descriptor
- int mIndent; // indentation level
- audio_utils_fifo * const mFifo; // FIFO itself,
- // non-NULL unless constructor fails
- audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO,
- // non-NULL unless constructor fails
-
- // TODO: it might be clearer, instead of a direct map from source location to vector of
- // timestamps, if we instead first mapped from source location to an object that
- // represented that location. And one_of its fields would be a vector of timestamps.
- // That would allow us to record other information about the source location beyond timestamps.
- void dumpLine(const String8& timestamp, String8& body);
-
- EntryIterator handleFormat(const FormatEntry &fmtEntry,
- String8 *timestamp,
- String8 *body);
- // dummy method for handling absent author entry
- virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {}
-
- // Searches for the last entry of type <type> in the range [front, back)
- // back has to be entry-aligned. Returns nullptr if none enconuntered.
- static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
- const std::set<Event> &types);
-
- static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps
-};
-
-// Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name
-class NamedReader {
public:
- NamedReader() { mName[0] = '\0'; } // for Vector
- NamedReader(const sp<NBLog::Reader>& reader, const char *name) :
- mReader(reader)
- { strlcpy(mName, name, sizeof(mName)); }
- ~NamedReader() { }
- const sp<NBLog::Reader>& reader() const { return mReader; }
- const char* name() const { return mName; }
-private:
- sp<NBLog::Reader> mReader;
- static const size_t kMaxName = 32;
- char mName[kMaxName];
-};
+ // ---------------------------------------------------------------------------
-// ---------------------------------------------------------------------------
+ // FIXME Timeline was intended to wrap Writer and Reader, but isn't actually used yet.
+ // For now it is just a namespace for sharedSize().
+ class Timeline : public RefBase {
+ public:
+#if 0
+ Timeline(size_t size, void *shared = NULL);
+ virtual ~Timeline();
+#endif
-class Merger : public RefBase {
-public:
- Merger(const void *shared, size_t size);
+ // Input parameter 'size' is the desired size of the timeline in byte units.
+ // Returns the size rounded up to a power-of-2, plus the constant size overhead for indices.
+ static size_t sharedSize(size_t size);
- virtual ~Merger() {}
+#if 0
+ private:
+ friend class Writer;
+ friend class Reader;
- void addReader(const NamedReader &reader);
- // TODO add removeReader
- void merge();
- // FIXME This is returning a reference to a shared variable that needs a lock
- const std::vector<NamedReader>& getNamedReaders() const;
-private:
- // vector of the readers the merger is supposed to merge from.
- // every reader reads from a writer's buffer
- // FIXME Needs to be protected by a lock
- std::vector<NamedReader> mNamedReaders;
+ const size_t mSize; // circular buffer size in bytes, must be a power of 2
+ bool mOwn; // whether I own the memory at mShared
+ Shared* const mShared; // pointer to shared memory
+#endif
+ };
- // TODO Need comments on all of these
- Shared * const mShared;
- std::unique_ptr<audio_utils_fifo> mFifo;
- std::unique_ptr<audio_utils_fifo_writer> mFifoWriter;
-};
+ // ---------------------------------------------------------------------------
-class MergeReader : public Reader {
-public:
- MergeReader(const void *shared, size_t size, Merger &merger);
-private:
- // FIXME Needs to be protected by a lock,
- // because even though our use of it is read-only there may be asynchronous updates
- const std::vector<NamedReader>& mNamedReaders;
- // handle author entry by looking up the author's name and appending it to the body
- // returns number of bytes read from fmtEntry
- void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
-};
+ // Writer is thread-safe with respect to Reader, but not with respect to multiple threads
+ // calling Writer methods. If you need multi-thread safety for writing, use LockedWriter.
+ class Writer : public RefBase {
+ public:
+ Writer(); // dummy nop implementation without shared memory
-// MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot:
-// when triggered, it awakes for a lapse of time, during which it periodically merges; if
-// retriggered, the timeout is reset.
-// The thread is triggered on AudioFlinger binder activity.
-class MergeThread : public Thread {
-public:
- MergeThread(Merger &merger);
- virtual ~MergeThread() override;
+ // Input parameter 'size' is the desired size of the timeline in byte units.
+ // The size of the shared memory must be at least Timeline::sharedSize(size).
+ Writer(void *shared, size_t size);
+ Writer(const sp<IMemory>& iMemory, size_t size);
- // Reset timeout and activate thread to merge periodically if it's idle
- void wakeup();
+ virtual ~Writer();
- // Set timeout period until the merging thread goes idle again
- void setTimeoutUs(int time);
+ // FIXME needs comments, and some should be private
+ virtual void log(const char *string);
+ virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+ virtual void logvf(const char *fmt, va_list ap);
+ virtual void logTimestamp();
+ virtual void logTimestamp(const int64_t ts);
+ virtual void logInteger(const int x);
+ virtual void logFloat(const float x);
+ virtual void logPID();
+ virtual void logFormat(const char *fmt, log_hash_t hash, ...);
+ virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap);
+ virtual void logStart(const char *fmt);
+ virtual void logEnd();
+ virtual void logHash(log_hash_t hash);
+ virtual void logEventHistTs(Event event, log_hash_t hash);
-private:
- virtual bool threadLoop() override;
+ virtual bool isEnabled() const;
- // the merger who actually does the work of merging the logs
- Merger& mMerger;
+ // return value for all of these is the previous isEnabled()
+ virtual bool setEnabled(bool enabled); // but won't enable if no shared memory
+ bool enable() { return setEnabled(true); }
+ bool disable() { return setEnabled(false); }
- // mutex for the condition variable
- Mutex mMutex;
+ sp<IMemory> getIMemory() const { return mIMemory; }
- // condition variable to activate merging on timeout >= 0
- Condition mCond;
+ private:
+ // 0 <= length <= kMaxLength
+ // writes a single Entry to the FIFO
+ void log(Event event, const void *data, size_t length);
+ // checks validity of an event before calling log above this one
+ void log(const Entry *entry, bool trusted = false);
- // time left until the thread blocks again (in microseconds)
- int mTimeoutUs;
+ Shared* const mShared; // raw pointer to shared memory
+ sp<IMemory> mIMemory; // ref-counted version, initialized in constructor
+ // and then const
+ audio_utils_fifo * const mFifo; // FIFO itself, non-NULL
+ // unless constructor fails
+ audio_utils_fifo_writer * const mFifoWriter; // used to write to FIFO, non-NULL
+ // unless dummy constructor used
+ bool mEnabled; // whether to actually log
- // merging period when the thread is awake
- static const int kThreadSleepPeriodUs = 1000000 /*1s*/;
+ // cached pid and process name to use in %p format specifier
+ // total tag length is mPidTagSize and process name is not zero terminated
+ char *mPidTag;
+ size_t mPidTagSize;
+ };
- // initial timeout value when triggered
- static const int kThreadWakeupPeriodUs = 3000000 /*3s*/;
-};
+ // ---------------------------------------------------------------------------
+
+ // Similar to Writer, but safe for multiple threads to call concurrently
+ class LockedWriter : public Writer {
+ public:
+ LockedWriter();
+ LockedWriter(void *shared, size_t size);
+
+ virtual void log(const char *string);
+ virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+ virtual void logvf(const char *fmt, va_list ap);
+ virtual void logTimestamp();
+ virtual void logTimestamp(const int64_t ts);
+ virtual void logInteger(const int x);
+ virtual void logFloat(const float x);
+ virtual void logPID();
+ virtual void logStart(const char *fmt);
+ virtual void logEnd();
+ virtual void logHash(log_hash_t hash);
+
+ virtual bool isEnabled() const;
+ virtual bool setEnabled(bool enabled);
+
+ private:
+ mutable Mutex mLock;
+ };
+
+ // ---------------------------------------------------------------------------
+
+ class Reader : public RefBase {
+ public:
+ // A snapshot of a readers buffer
+ // This is raw data. No analysis has been done on it
+ class Snapshot {
+ public:
+ Snapshot() : mData(NULL), mLost(0) {}
+
+ Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
+
+ ~Snapshot() { delete[] mData; }
+
+ // copy of the buffer
+ uint8_t *data() const { return mData; }
+
+ // amount of data lost (given by audio_utils_fifo_reader)
+ size_t lost() const { return mLost; }
+
+ // iterator to beginning of readable segment of snapshot
+ // data between begin and end has valid entries
+ EntryIterator begin() { return mBegin; }
+
+ // iterator to end of readable segment of snapshot
+ EntryIterator end() { return mEnd; }
+
+ private:
+ friend class MergeReader;
+ friend class Reader;
+ uint8_t *mData;
+ size_t mLost;
+ EntryIterator mBegin;
+ EntryIterator mEnd;
+ };
+
+ // Input parameter 'size' is the desired size of the timeline in byte units.
+ // The size of the shared memory must be at least Timeline::sharedSize(size).
+ Reader(const void *shared, size_t size);
+ Reader(const sp<IMemory>& iMemory, size_t size);
+
+ virtual ~Reader();
+
+ // get snapshot of readers fifo buffer, effectively consuming the buffer
+ std::unique_ptr<Snapshot> getSnapshot();
+
+ bool isIMemory(const sp<IMemory>& iMemory) const;
+
+ protected:
+ // print a summary of the performance to the console
+ void dumpLine(const String8& timestamp, String8& body);
+ EntryIterator handleFormat(const FormatEntry &fmtEntry,
+ String8 *timestamp,
+ String8 *body);
+ int mFd; // file descriptor
+ int mIndent; // indentation level
+ int mLost; // bytes of data lost before buffer was read
+
+ private:
+ static const std::set<Event> startingTypes;
+ static const std::set<Event> endingTypes;
+
+ // declared as const because audio_utils_fifo() constructor
+ sp<IMemory> mIMemory; // ref-counted version, assigned only in constructor
+
+ /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not
+ audio_utils_fifo * const mFifo; // FIFO itself,
+ // non-NULL unless constructor fails
+ audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO,
+ // non-NULL unless constructor fails
+
+ // TODO: it might be clearer, instead of a direct map from source location to vector of
+ // timestamps, if we instead first mapped from source location to an object that
+ // represented that location. And one_of its fields would be a vector of timestamps.
+ // That would allow us to record other information about the source location beyond
+ // timestamps.
+
+ // Searches for the last entry of type <type> in the range [front, back)
+ // back has to be entry-aligned. Returns nullptr if none enconuntered.
+ static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
+ const std::set<Event> &types);
+
+ // dummy method for handling absent author entry
+ virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {}
+ };
+
+ // Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name
+ class NamedReader {
+ public:
+ NamedReader() { mName[0] = '\0'; } // for Vector
+ NamedReader(const sp<NBLog::Reader>& reader, const char *name) :
+ mReader(reader)
+ { strlcpy(mName, name, sizeof(mName)); }
+ ~NamedReader() { }
+ const sp<NBLog::Reader>& reader() const { return mReader; }
+ const char* name() const { return mName; }
+
+ private:
+ sp<NBLog::Reader> mReader;
+ static const size_t kMaxName = 32;
+ char mName[kMaxName];
+ };
+
+ // ---------------------------------------------------------------------------
+
+ // This class is used to read data from each thread's individual FIFO in shared memory
+ // and write it to a single FIFO in local memory.
+ class Merger : public RefBase {
+ public:
+ Merger(const void *shared, size_t size);
+
+ virtual ~Merger() {}
+
+ void addReader(const NamedReader &reader);
+ // TODO add removeReader
+ void merge();
+
+ // FIXME This is returning a reference to a shared variable that needs a lock
+ const std::vector<NamedReader>& getNamedReaders() const;
+
+ private:
+ // vector of the readers the merger is supposed to merge from.
+ // every reader reads from a writer's buffer
+ // FIXME Needs to be protected by a lock
+ std::vector<NamedReader> mNamedReaders;
+
+ Shared * const mShared; // raw pointer to shared memory
+ std::unique_ptr<audio_utils_fifo> mFifo; // FIFO itself
+ std::unique_ptr<audio_utils_fifo_writer> mFifoWriter; // used to write to FIFO
+ };
+
+ // This class has a pointer to the FIFO in local memory which stores the merged
+ // data collected by NBLog::Merger from all NamedReaders. It is used to process
+ // this data and write the result to PerformanceAnalysis.
+ class MergeReader : public Reader {
+ public:
+ MergeReader(const void *shared, size_t size, Merger &merger);
+
+ void dump(int fd, int indent = 0);
+ // process a particular snapshot of the reader
+ void getAndProcessSnapshot(Snapshot & snap);
+ // call getSnapshot of the content of the reader's buffer and process the data
+ void getAndProcessSnapshot();
+
+ private:
+ // FIXME Needs to be protected by a lock,
+ // because even though our use of it is read-only there may be asynchronous updates
+ const std::vector<NamedReader>& mNamedReaders;
+
+ // analyzes, compresses and stores the merged data
+ // contains a separate instance for every author (thread), and for every source file
+ // location within each author
+ ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis;
+
+ // handle author entry by looking up the author's name and appending it to the body
+ // returns number of bytes read from fmtEntry
+ void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
+ };
+
+ // MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot:
+ // when triggered, it awakes for a lapse of time, during which it periodically merges; if
+ // retriggered, the timeout is reset.
+ // The thread is triggered on AudioFlinger binder activity.
+ class MergeThread : public Thread {
+ public:
+ MergeThread(Merger &merger, MergeReader &mergeReader);
+ virtual ~MergeThread() override;
+
+ // Reset timeout and activate thread to merge periodically if it's idle
+ void wakeup();
+
+ // Set timeout period until the merging thread goes idle again
+ void setTimeoutUs(int time);
+
+ private:
+ virtual bool threadLoop() override;
+
+ // the merger who actually does the work of merging the logs
+ Merger& mMerger;
+
+ // the mergereader used to process data merged by mMerger
+ MergeReader& mMergeReader;
+
+ // mutex for the condition variable
+ Mutex mMutex;
+
+ // condition variable to activate merging on timeout >= 0
+ Condition mCond;
+
+ // time left until the thread blocks again (in microseconds)
+ int mTimeoutUs;
+
+ // merging period when the thread is awake
+ static const int kThreadSleepPeriodUs = 1000000 /*1s*/;
+
+ // initial timeout value when triggered
+ static const int kThreadWakeupPeriodUs = 3000000 /*3s*/;
+ };
}; // class NBLog
diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
index b0dc148..81f9c58 100644
--- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
+++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
@@ -19,16 +19,22 @@
#ifndef ANDROID_MEDIA_PERFORMANCEANALYSIS_H
#define ANDROID_MEDIA_PERFORMANCEANALYSIS_H
-#include <map>
#include <deque>
+#include <map>
#include <vector>
-#include "NBLog.h"
-#include "ReportPerformance.h"
+
+#include <media/nbaio/ReportPerformance.h>
namespace android {
namespace ReportPerformance {
+class PerformanceAnalysis;
+
+// a map of PerformanceAnalysis instances
+// The outer key is for the thread, the inner key for the source file location.
+using PerformanceAnalysisMap = std::map<int, std::map<log_hash_t, PerformanceAnalysis>>;
+
class PerformanceAnalysis {
// This class stores and analyzes audio processing wakeup timestamps from NBLog
// FIXME: currently, all performance data is stored in deques. Need to add a mutex.
@@ -38,6 +44,9 @@
PerformanceAnalysis();
+ friend void dump(int fd, int indent,
+ PerformanceAnalysisMap &threadPerformanceAnalysis);
+
// Given a series of audio processing wakeup timestamps,
// compresses and and analyzes the data, and flushes
// the timestamp series from memory.
@@ -49,10 +58,6 @@
// effectively discarding the idle audio time interval
void handleStateChange();
- // When the short-term histogram array mRecentHists has reached capacity,
- // merges histograms for data compression and stores them in mLongTermHists
- void processAndFlushRecentHists();
-
// Writes wakeup timestamp entry to log and runs analysis
// TODO: make this thread safe. Each thread should have its own instance
// of PerformanceAnalysis.
@@ -74,11 +79,10 @@
// FIXME: move this data visualization to a separate class. Model/view/controller
void reportPerformance(String8 *body, int maxHeight = 10);
- // TODO: delete this. temp for testing
- void testFunction();
-
- // This function used to detect glitches in a time series
- // TODO incorporate this into the analysis (currently unused)
+ // This function detects glitches in a time series.
+ // TODO: decide whether to use this or whether it is overkill, and it is enough
+ // to only treat as glitches single wakeup call intervals which are too long.
+ // Ultimately, glitch detection will be directly on the audio signal.
void alertIfGlitch(const std::vector<timestamp_raw> &samples);
private:
@@ -90,23 +94,17 @@
// a peak is a moment at which the average outlier interval changed significantly
std::deque<timestamp> mPeakTimestamps;
- // TODO: turn these into circular buffers for better data flow
- // FIFO of small histograms
- // stores fixed-size short buffer period histograms with timestamp of first sample
- std::deque<std::pair<timestamp, Histogram>> mRecentHists;
+ // stores stores buffer period histograms with timestamp of first sample
+ // TODO use a circular buffer
+ std::deque<std::pair<timestamp, Histogram>> mHists;
- // FIFO of small histograms
- // stores fixed-size long-term buffer period histograms with timestamp of first sample
- std::deque<std::pair<timestamp, Histogram>> mLongTermHists;
-
- // vector of timestamps, collected from NBLog for a (TODO) specific thread
+ // vector of timestamps, collected from NBLog for a specific thread
// when a vector reaches its maximum size, the data is processed and flushed
std::vector<timestamp_raw> mTimeStampSeries;
- static const int kMsPerSec = 1000;
-
// Parameters used when detecting outliers
// TODO: learn some of these from the data, delete unused ones
+ // TODO: put used variables in a struct
// FIXME: decide whether to make kPeriodMs static.
static const int kNumBuff = 3; // number of buffers considered in local history
int kPeriodMs; // current period length is ideally 4 ms
@@ -115,30 +113,34 @@
static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
int kPeriodMsCPU; // compute based on kPeriodLen and kRatio
- // Peak detection: number of standard deviations from mean considered a significant change
- static const int kStddevThreshold = 5;
-
// capacity allocated to data structures
- // TODO: adjust all of these values
- static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
- static const int kShortHistSize = 50; // number of samples in a short-term histogram
- static const int kOutlierSeriesSize = 100; // number of values stored in outlier array
- static const int kPeakSeriesSize = 100; // number of values stored in peak array
- static const int kLongTermHistsCapacity = 20; // number of long-term histogram stored in memory
- // maximum elapsed time between first and last timestamp of a long-term histogram
- static const int kMaxHistTimespanMs = 5 * kMsPerSec;
+ // TODO: make these values longer when testing is finished
+ struct MaxLength {
+ size_t Hists; // number of histograms stored in memory
+ size_t TimeStamps; // histogram size, e.g. maximum length of timestamp series
+ size_t Outliers; // number of values stored in outlier array
+ size_t Peaks; // number of values stored in peak array
+ // maximum elapsed time between first and last timestamp of a long-term histogram
+ int HistTimespanMs;
+ };
+ static constexpr MaxLength kMaxLength = {.Hists = 20, .TimeStamps = 1000,
+ .Outliers = 100, .Peaks = 100, .HistTimespanMs = 5 * kMsPerSec };
// these variables are stored in-class to ensure continuity while analyzing the timestamp
// series one short sequence at a time: the variables are not re-initialized every time.
- // FIXME: create inner class for these variables and decide which other ones to add to it
- double mPeakDetectorMean = -1;
- double mPeakDetectorSd = -1;
- // variables for storeOutlierData
- uint64_t mElapsed = 0;
- int64_t mPrevNs = -1;
-
+ struct OutlierDistribution {
+ double Mean = -1;
+ double Sd = -1;
+ uint64_t Elapsed = 0;
+ int64_t PrevNs = -1;
+ // number of standard deviations from mean considered a significant change
+ const int kMaxDeviation = 5;
+ } mOutlierDistribution;
};
+void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis);
+void dumpLine(int fd, int indent, const String8 &body);
+
} // namespace ReportPerformance
} // namespace android
diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h
index 27d2810..ed97a23 100644
--- a/media/libnbaio/include/media/nbaio/ReportPerformance.h
+++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h
@@ -24,11 +24,13 @@
namespace android {
// This class is used by reportPerformance function
-// TODO move reportPerformance function to ReportPerformance.cpp
+// TODO move PerformanceAnalysis::reportPerformance function to ReportPerformance.cpp
class String8;
namespace ReportPerformance {
+const int kMsPerSec = 1000;
+
// stores a histogram: key: observed buffer period. value: count
// TODO: unsigned, unsigned
using Histogram = std::map<int, int>;
@@ -52,12 +54,14 @@
return 31 - __builtin_clz(x);
}
+// TODO: delete dump in NBLog::Reader and add it here
+
// Writes outlier intervals, timestamps, and histograms spanning long time
// intervals to a file.
-void writeToFile(std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
- std::deque<std::pair<timestamp, Histogram>> &hists,
- const char * kName,
- bool append);
+void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+ const std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
+ const std::deque<timestamp> &peakTimestamps,
+ const char * kDirectory, bool append, int author, log_hash_t hash);
} // namespace ReportPerformance
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 19973bd..7070bdb 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -156,6 +156,7 @@
}
subdirs = [
+ "codec2",
"codecs/*",
"colorconversion",
"filters",
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
new file mode 100644
index 0000000..e5bc4b3
--- /dev/null
+++ b/media/libstagefright/codec2/Android.bp
@@ -0,0 +1,27 @@
+cc_library_shared {
+ name: "libstagefright_codec2",
+
+ srcs: ["C2.cpp"],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/native/include/media/hardware",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
+
+subdirs = [
+ "tests",
+]
diff --git a/media/libstagefright/codec2/Android.mk b/media/libstagefright/codec2/Android.mk
deleted file mode 100644
index ef06ed7..0000000
--- a/media/libstagefright/codec2/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- C2.cpp \
-
-LOCAL_C_INCLUDES += \
- $(TOP)/frameworks/av/media/libstagefright/codec2/include \
- $(TOP)/frameworks/native/include/media/hardware \
-
-LOCAL_MODULE:= libstagefright_codec2
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CLANG := true
-LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
-LOCAL_SANITIZE_DIAG := cfi
-
-include $(BUILD_SHARED_LIBRARY)
-
-################################################################################
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codec2/tests/Android.bp b/media/libstagefright/codec2/tests/Android.bp
new file mode 100644
index 0000000..1dc6a58
--- /dev/null
+++ b/media/libstagefright/codec2/tests/Android.bp
@@ -0,0 +1,31 @@
+cc_test {
+ name: "codec2_test",
+
+ tags: [
+ "tests",
+ ],
+
+ srcs: [
+ "vndk/C2UtilTest.cpp",
+ "C2_test.cpp",
+ "C2Param_test.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/av/media/libstagefright/codec2/vndk/include",
+ "frameworks/native/include/media/openmax",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libstagefright_codec2",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-std=c++14",
+ ],
+}
diff --git a/media/libstagefright/codec2/tests/Android.mk b/media/libstagefright/codec2/tests/Android.mk
deleted file mode 100644
index 49c4253..0000000
--- a/media/libstagefright/codec2/tests/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE := codec2_test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := \
- vndk/C2UtilTest.cpp \
- C2_test.cpp \
- C2Param_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libstagefright_codec2 \
- liblog
-
-LOCAL_C_INCLUDES := \
- frameworks/av/media/libstagefright/codec2/include \
- frameworks/av/media/libstagefright/codec2/vndk/include \
- $(TOP)/frameworks/native/include/media/openmax \
-
-LOCAL_CFLAGS += -Werror -Wall -std=c++14
-LOCAL_CLANG := true
-
-include $(BUILD_NATIVE_TEST)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index d2b2454..fcf1092 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -64,10 +64,9 @@
sp<Fence> fence;
::android::FrameEventHistoryDelta outTimestamps;
status_t status = mBase->dequeueBuffer(
- &slot, &fence,
- width, height,
- static_cast<::android::PixelFormat>(format), usage,
- getFrameTimestamps ? &outTimestamps : nullptr);
+ &slot, &fence, width, height,
+ static_cast<::android::PixelFormat>(format), usage, nullptr,
+ getFrameTimestamps ? &outTimestamps : nullptr);
hidl_handle tFence;
FrameEventHistoryDelta tOutTimestamps;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0df9a39..3a95a3b 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2603,7 +2603,7 @@
while (ec->mEffects.size()) {
sp<EffectModule> effect = ec->mEffects[0];
effect->unPin();
- t->removeEffect_l(effect);
+ t->removeEffect_l(effect, /*release*/ true);
if (effect->purgeHandles()) {
t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f2c1c4f..bd5f146 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
+#include <media/AudioEffect.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
@@ -109,7 +110,10 @@
{
ALOGV("Destructor %p", this);
if (mEffectInterface != 0) {
- ALOGW("EffectModule %p destructor called with unreleased interface", this);
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
+ this, uuidStr);
release_l();
}
@@ -1081,18 +1085,12 @@
result.append(buffer);
result.append("\t\tDescriptor:\n");
- snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
- mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
- mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],
- mDescriptor.uuid.node[2],
- mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ snprintf(buffer, SIZE, "\t\t- UUID: %s\n", uuidStr);
result.append(buffer);
- snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
- mDescriptor.type.timeLow, mDescriptor.type.timeMid,
- mDescriptor.type.timeHiAndVersion,
- mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],
- mDescriptor.type.node[2],
- mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+ AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
+ snprintf(buffer, SIZE, "\t\t- TYPE: %s\n", uuidStr);
result.append(buffer);
snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
mDescriptor.apiVersion,
@@ -1306,11 +1304,10 @@
if (thread != 0) {
thread->disconnectEffectHandle(this, unpinIfLast);
} else {
- ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
// try to cleanup as much as we can
sp<EffectModule> effect = mEffect.promote();
- if (effect != 0) {
- effect->disconnectHandle(this, unpinIfLast);
+ if (effect != 0 && effect->disconnectHandle(this, unpinIfLast) > 0) {
+ ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
}
}
diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml
index 7af2f81..73efe8e 100644
--- a/services/audiopolicy/config/audio_policy_configuration.xml
+++ b/services/audiopolicy/config/audio_policy_configuration.xml
@@ -163,37 +163,16 @@
sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>
<route type="mix" sink="Wired Headphones"
sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>
- <route type="mix" sink="Telephony Tx"
- sources="voice_tx"/>
<route type="mix" sink="primary input"
sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic"/>
<route type="mix" sink="Telephony Tx"
- sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic"/>
+ sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic, voice_tx"/>
<route type="mix" sink="voice_rx"
sources="Telephony Rx"/>
</routes>
</module>
- <!-- HDMI Audio HAL -->
- <module description="HDMI Audio HAL" name="hdmi" version="2.0">
- <mixPorts>
- <mixPort name="hdmi output" role="source">
- <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000"/>
- </mixPort>
- </mixPorts>
- <devicePorts>
- <devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink">
- <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
- </devicePort>
- </devicePorts>
- <routes>
- <route type="mix" sink="HDMI Out"
- sources="hdmi output"/>
- </routes>
- </module>
-
<!-- A2dp Audio HAL -->
<xi:include href="a2dp_audio_policy_configuration.xml"/>
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index a5512e1..fe19b6b 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -26,13 +26,19 @@
namespace android {
- static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
+static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n";
+
+// mMerger, mMergeReader, and mMergeThread all point to the same location in memory
+// mMergerShared. This is the local memory FIFO containing data merged from all
+// individual thread FIFOs in shared memory. mMergeThread is used to periodically
+// call NBLog::Merger::merge() to collect the data and write it to the FIFO, and call
+// NBLog::MergeReader::getAndProcessSnapshot to process the merged data.
MediaLogService::MediaLogService() :
BnMediaLogService(),
mMergerShared((NBLog::Shared*) malloc(NBLog::Timeline::sharedSize(kMergeBufferSize))),
mMerger(mMergerShared, kMergeBufferSize),
mMergeReader(mMergerShared, kMergeBufferSize, mMerger),
- mMergeThread(new NBLog::MergeThread(mMerger))
+ mMergeThread(new NBLog::MergeThread(mMerger, mMergeReader))
{
mMergeThread->run("MergeThread");
}
@@ -123,15 +129,10 @@
} else {
ALOGI("%s:", namedReader.name());
}
- // TODO This code is for testing, remove it when done
- // namedReader.reader()->dump(fd, 0 /*indent*/);
}
-
mLock.unlock();
}
}
-
- // FIXME request merge to make sure log is up to date
mMergeReader.dump(fd);
return NO_ERROR;
}