dumpsys: add --json option to media.audio_flinger.
Currently only extracting audio performance data.
Test: dumpsys media.audio_flinger --json
Bug: 68148948
Change-Id: If207e7e20683dcf8d8d5874417cd7466c78a5df0
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 53a4ce9..0237aee 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -20,9 +20,11 @@
//#define LOG_NDEBUG 0
#include "Configuration.h"
+#include <algorithm> // std::any_of
#include <dirent.h>
#include <math.h>
#include <signal.h>
+#include <string>
#include <sys/time.h>
#include <sys/resource.h>
@@ -434,6 +436,15 @@
if (!dumpAllowed()) {
dumpPermissionDenial(fd, args);
} else {
+ // XXX This is sort of hacky for now.
+ const bool formatJson = std::any_of(args.begin(), args.end(),
+ [](const String16 &arg) { return arg == String16("--json"); });
+ if (formatJson) {
+ // XXX consider buffering if the string happens to be too long.
+ dprintf(fd, "%s", getJsonString().c_str());
+ return NO_ERROR;
+ }
+
// get state of hardware lock
bool hardwareLocked = dumpTryLock(mHardwareLock);
if (!hardwareLocked) {
@@ -443,7 +454,7 @@
mHardwareLock.unlock();
}
- bool locked = dumpTryLock(mLock);
+ const bool locked = dumpTryLock(mLock);
// failed to lock - AudioFlinger is probably deadlocked
if (!locked) {
@@ -545,6 +556,36 @@
return NO_ERROR;
}
+std::string AudioFlinger::getJsonString()
+{
+ std::string jsonStr = "{\n";
+ const bool locked = dumpTryLock(mLock);
+
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ jsonStr += " \"deadlock_message\": ";
+ jsonStr += kDeadlockedString;
+ jsonStr += ",\n";
+ }
+ // FIXME risky to access data structures without a lock held?
+
+ jsonStr += " \"Playback_Threads\": [\n";
+ // dump playback threads
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (i != 0) {
+ jsonStr += ",\n";
+ }
+ jsonStr += mPlaybackThreads.valueAt(i)->getJsonString();
+ }
+ jsonStr += "\n ]\n}\n";
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ return jsonStr;
+}
+
sp<AudioFlinger::Client> AudioFlinger::registerPid(pid_t pid)
{
Mutex::Autolock _cl(mClientLock);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 0276cad..fb83d75 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -23,6 +23,8 @@
#include <mutex>
#include <deque>
#include <map>
+#include <memory>
+#include <string>
#include <vector>
#include <stdint.h>
#include <sys/types.h>
@@ -111,6 +113,7 @@
static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
+ std::string getJsonString();
// IAudioFlinger interface, in binder opcode order
virtual sp<IAudioTrack> createTrack(const CreateTrackInput& input,
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
index ede8e3f..64bb426 100644
--- a/services/audioflinger/Configuration.h
+++ b/services/audioflinger/Configuration.h
@@ -27,7 +27,7 @@
//#define AUDIO_WATCHDOG
// uncomment to display CPU load adjusted for CPU frequency
-//#define CPU_FREQUENCY_STATISTICS
+#define CPU_FREQUENCY_STATISTICS
// uncomment to enable fast threads to take performance samples for later statistical analysis
#define FAST_THREAD_STATISTICS
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index d60643c..c0f9f0f 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -24,6 +24,7 @@
#include <cpustats/ThreadCpuUsage.h>
#endif
#endif
+#include <string>
#include <utils/Debug.h>
#include <utils/Log.h>
#include "FastMixerDumpState.h"
@@ -76,14 +77,14 @@
dprintf(fd, " FastMixer Timestamp stats: %s\n", mTimestampVerifier.toString().c_str());
#ifdef FAST_THREAD_STATISTICS
// find the interval of valid samples
- uint32_t bounds = mBounds;
- uint32_t newestOpen = bounds & 0xFFFF;
+ const uint32_t bounds = mBounds;
+ const uint32_t newestOpen = bounds & 0xFFFF;
uint32_t oldestClosed = bounds >> 16;
//uint32_t n = (newestOpen - oldestClosed) & 0xFFFF;
uint32_t n;
__builtin_sub_overflow(newestOpen, oldestClosed, &n);
- n = n & 0xFFFF;
+ n &= 0xFFFF;
if (n > mSamplingN) {
ALOGE("too many samples %u", n);
@@ -204,4 +205,56 @@
}
}
+// TODO get rid of extraneous lines and use better key names.
+// TODO may go back to using a library to do the json formatting.
+std::string FastMixerDumpState::getJsonString() const
+{
+ if (mCommand == FastMixerState::INITIAL) {
+ return " {\n \"status\": \"uninitialized\"\n }";
+ }
+ std::string jsonStr = " {\n";
+#ifdef FAST_THREAD_STATISTICS
+ // find the interval of valid samples
+ const uint32_t bounds = mBounds;
+ const uint32_t newestOpen = bounds & 0xFFFF;
+ uint32_t oldestClosed = bounds >> 16;
+
+ //uint32_t n = (newestOpen - oldestClosed) & 0xFFFF;
+ uint32_t n;
+ __builtin_sub_overflow(newestOpen, oldestClosed, &n);
+ n &= 0xFFFF;
+
+ if (n > mSamplingN) {
+ ALOGE("too many samples %u", n);
+ n = mSamplingN;
+ }
+ // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency,
+ // and adjusted CPU load in MHz normalized for CPU clock frequency
+ std::string jsonWallStr = " \"wall_clock_time\":[";
+ std::string jsonLoadNsStr = " \"raw_cpu_load\":[";
+ // loop over all the samples
+ for (uint32_t j = 0; j < n; ++j) {
+ size_t i = oldestClosed++ & (mSamplingN - 1);
+ uint32_t wallNs = mMonotonicNs[i];
+ if (j != 0) {
+ jsonWallStr += ',';
+ jsonLoadNsStr += ',';
+ }
+ /* jsonObject["wall"].append(wallNs); */
+ jsonWallStr += std::to_string(wallNs);
+ uint32_t sampleLoadNs = mLoadNs[i];
+ jsonLoadNsStr += std::to_string(sampleLoadNs);
+ }
+ jsonWallStr += ']';
+ jsonLoadNsStr += ']';
+ if (n) {
+ jsonStr += jsonWallStr + ",\n" + jsonLoadNsStr + "\n";
+ } else {
+ //dprintf(fd, " No FastMixer statistics available currently\n");
+ }
+#endif
+ jsonStr += " }";
+ return jsonStr;
+}
+
} // android
diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h
index 9b91cbc..81c4175 100644
--- a/services/audioflinger/FastMixerDumpState.h
+++ b/services/audioflinger/FastMixerDumpState.h
@@ -18,6 +18,7 @@
#define ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H
#include <stdint.h>
+#include <string>
#include <audio_utils/TimestampVerifier.h>
#include "Configuration.h"
#include "FastThreadDumpState.h"
@@ -65,7 +66,8 @@
FastMixerDumpState();
/*virtual*/ ~FastMixerDumpState();
- void dump(int fd) const; // should only be called on a stable copy, not the original
+ void dump(int fd) const; // should only be called on a stable copy, not the original
+ std::string getJsonString() const; // should only be called on a stable copy, not the original
double mLatencyMs = 0.; // measured latency, default of 0 if no valid timestamp read.
uint32_t mWriteSequence; // incremented before and after each write()
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 70af5c6..5cc8c31 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -23,6 +23,8 @@
#include "Configuration.h"
#include <math.h>
#include <fcntl.h>
+#include <memory>
+#include <string>
#include <linux/futex.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -1760,6 +1762,11 @@
mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */);
}
+std::string AudioFlinger::PlaybackThread::getJsonString() const
+{
+ return "{}";
+}
+
void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args __unused)
{
String8 result;
@@ -5098,9 +5105,8 @@
// while we are dumping it. It may be inconsistent, but it won't mutate!
// This is a large object so we place it on the heap.
// FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages.
- const FastMixerDumpState *copy = new FastMixerDumpState(mFastMixerDumpState);
+ const std::unique_ptr<FastMixerDumpState> copy(new FastMixerDumpState(mFastMixerDumpState));
copy->dump(fd);
- delete copy;
#ifdef STATE_QUEUE_DUMP
// Similar for state queue
@@ -5123,6 +5129,16 @@
}
}
+std::string AudioFlinger::MixerThread::getJsonString() const
+{
+ // Make a non-atomic copy of fast mixer dump state so it won't change underneath us
+ // while we are dumping it. It may be inconsistent, but it won't mutate!
+ // This is a large object so we place it on the heap.
+ // FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages.
+ return std::unique_ptr<FastMixerDumpState>(new FastMixerDumpState(mFastMixerDumpState))
+ ->getJsonString();
+}
+
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
{
return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000) / 2;
@@ -7423,9 +7439,8 @@
// while we are dumping it. It may be inconsistent, but it won't mutate!
// This is a large object so we place it on the heap.
// FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages.
- const FastCaptureDumpState *copy = new FastCaptureDumpState(mFastCaptureDumpState);
+ std::unique_ptr<FastCaptureDumpState> copy(new FastCaptureDumpState(mFastCaptureDumpState));
copy->dump(fd);
- delete copy;
}
void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused)
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 064e291..734a7bd 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -658,6 +658,8 @@
virtual ~PlaybackThread();
void dump(int fd, const Vector<String16>& args);
+ // returns a string of audio performance related data in JSON format.
+ virtual std::string getJsonString() const;
// Thread virtuals
virtual bool threadLoop();
@@ -1103,6 +1105,7 @@
virtual bool checkForNewParameter_l(const String8& keyValuePair,
status_t& status);
virtual void dumpInternals(int fd, const Vector<String16>& args);
+ std::string getJsonString() const override;
virtual bool isTrackAllowed_l(
audio_channel_mask_t channelMask, audio_format_t format,