Loading services/surfaceflinger/TimeStats/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ cc_library_shared { "libcutils", "liblog", "libprotobuf-cpp-lite", "libprotoutil", "libstatslog", "libstatspull", "libstatssocket", Loading @@ -17,6 +18,7 @@ cc_library_shared { ], export_include_dirs: ["."], export_shared_lib_headers: [ "libprotoutil", "libstatslog", "libstatspull", "libstatssocket", Loading services/surfaceflinger/TimeStats/TimeStats.cpp +118 −22 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include "TimeStats.h" #include <android-base/stringprintf.h> #include <android/util/ProtoOutputStream.h> #include <log/log.h> #include <utils/String8.h> #include <utils/Timers.h> Loading @@ -36,42 +37,134 @@ namespace android { namespace impl { status_pull_atom_return_t TimeStats::pullGlobalAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie) { status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie) { impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie); if (atom_tag != android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) { if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) { return timeStats->populateGlobalAtom(data); } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) { return timeStats->populateLayerAtom(data); } return STATS_PULL_SKIP; } std::lock_guard<std::mutex> lock(timeStats->mMutex); status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) { std::lock_guard<std::mutex> lock(mMutex); const auto& stats = timeStats->mTimeStats; if (stats.statsStart == 0) { if (mTimeStats.statsStart == 0) { return STATS_PULL_SKIP; } timeStats->flushPowerTimeLocked(); flushPowerTimeLocked(); struct stats_event* event = timeStats->mStatsDelegate->addStatsEventToPullData(data); timeStats->mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.totalFrames); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.missedFrames); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.clientCompositionFrames); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.displayOnTime); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.presentToPresent.totalTime()); timeStats->mStatsDelegate->statsEventBuild(event); timeStats->clearGlobalLocked(); struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data); mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime()); mStatsDelegate->statsEventBuild(event); clearGlobalLocked(); return STATS_PULL_SUCCESS; } TimeStats::TimeStats() : TimeStats(nullptr) {} namespace { // Histograms align with the order of fields in SurfaceflingerStatsLayerInfo. const std::array<std::string, 6> kHistogramNames = { "present2present", "post2present", "acquire2present", "latch2present", "desired2present", "post2acquire", }; std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram, size_t maxPulledHistogramBuckets) { auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end()); std::sort(buckets.begin(), buckets.end(), [](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) { return left.second > right.second; }); util::ProtoOutputStream proto; int histogramSize = 0; for (const auto& bucket : buckets) { if (++histogramSize > maxPulledHistogramBuckets) { break; } proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED | 1 /* field id */, (int32_t)bucket.first); proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | 2 /* field id */, (int64_t)bucket.second); } std::string byteString; proto.serializeToString(&byteString); return byteString; } } // namespace status_pull_atom_return_t TimeStats::populateLayerAtom(pulled_stats_event_list* data) { std::lock_guard<std::mutex> lock(mMutex); std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats; for (const auto& ele : mTimeStats.stats) { dumpStats.push_back(&ele.second); } std::sort(dumpStats.begin(), dumpStats.end(), [](TimeStatsHelper::TimeStatsLayer const* l, TimeStatsHelper::TimeStatsLayer const* r) { return l->totalFrames > r->totalFrames; }); TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate) { if (mMaxPulledLayers < dumpStats.size()) { dumpStats.resize(mMaxPulledLayers); } for (const auto& layer : dumpStats) { struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data); mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO); mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str()); mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames); mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames); for (const auto& name : kHistogramNames) { const auto& histogram = layer->deltas.find(name); if (histogram == layer->deltas.cend()) { mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0); } else { std::string bytes = histogramToProtoByteString(histogram->second.hist, mMaxPulledHistogramBuckets); mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(), bytes.size()); } } mStatsDelegate->statsEventBuild(event); } clearLayersLocked(); return STATS_PULL_SUCCESS; } TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {} TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate, std::optional<size_t> maxPulledLayers, std::optional<size_t> maxPulledHistogramBuckets) { if (statsDelegate != nullptr) { mStatsDelegate = std::move(statsDelegate); } if (maxPulledLayers) { mMaxPulledLayers = *maxPulledLayers; } if (maxPulledHistogramBuckets) { mMaxPulledHistogramBuckets = *maxPulledHistogramBuckets; } } void TimeStats::onBootFinished() { Loading Loading @@ -634,7 +727,9 @@ void TimeStats::enable() { mTimeStats.statsStart = static_cast<int64_t>(std::time(0)); mPowerTime.prevTime = systemTime(); mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, TimeStats::pullGlobalAtomCallback, nullptr, this); TimeStats::pullAtomCallback, nullptr, this); mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO, TimeStats::pullAtomCallback, nullptr, this); ALOGD("Enabled"); } Loading @@ -649,6 +744,7 @@ void TimeStats::disable() { mTimeStats.statsEnd = static_cast<int64_t>(std::time(0)); mStatsDelegate->unregisterStatsPullAtomCallback( android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO); ALOGD("Disabled"); } Loading services/surfaceflinger/TimeStats/TimeStats.h +18 −4 Original line number Diff line number Diff line Loading @@ -165,10 +165,21 @@ public: return stats_event_write_int64(event, field); } virtual void statsEventWriteString8(struct stats_event* event, const char* field) { return stats_event_write_string8(event, field); } virtual void statsEventWriteByteArray(struct stats_event* event, const uint8_t* buf, size_t numBytes) { return stats_event_write_byte_array(event, buf, numBytes); } virtual void statsEventBuild(struct stats_event* event) { return stats_event_build(event); } }; // For testing only for injecting custom dependencies. TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate); TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate, std::optional<size_t> maxPulledLayers, std::optional<size_t> maxPulledHistogramBuckets); void onBootFinished() override; void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override; Loading Loading @@ -207,9 +218,10 @@ public: static const size_t MAX_NUM_TIME_RECORDS = 64; private: static status_pull_atom_return_t pullGlobalAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie); static status_pull_atom_return_t pullAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie); status_pull_atom_return_t populateGlobalAtom(pulled_stats_event_list* data); status_pull_atom_return_t populateLayerAtom(pulled_stats_event_list* data); bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord); void flushAvailableRecordsToStatsLocked(int32_t layerId); void flushPowerTimeLocked(); Loading @@ -233,6 +245,8 @@ private: static const size_t MAX_NUM_LAYER_RECORDS = 200; static const size_t MAX_NUM_LAYER_STATS = 200; std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>(); size_t mMaxPulledLayers = 8; size_t mMaxPulledHistogramBuckets = 6; }; } // namespace impl Loading services/surfaceflinger/tests/unittests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ cc_test { "perfetto_trace_protos", ], shared_libs: [ "libprotoutil", "libstatssocket", "libsurfaceflinger", "libtimestats", Loading services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +279 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/surfaceflinger/TimeStats/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ cc_library_shared { "libcutils", "liblog", "libprotobuf-cpp-lite", "libprotoutil", "libstatslog", "libstatspull", "libstatssocket", Loading @@ -17,6 +18,7 @@ cc_library_shared { ], export_include_dirs: ["."], export_shared_lib_headers: [ "libprotoutil", "libstatslog", "libstatspull", "libstatssocket", Loading
services/surfaceflinger/TimeStats/TimeStats.cpp +118 −22 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include "TimeStats.h" #include <android-base/stringprintf.h> #include <android/util/ProtoOutputStream.h> #include <log/log.h> #include <utils/String8.h> #include <utils/Timers.h> Loading @@ -36,42 +37,134 @@ namespace android { namespace impl { status_pull_atom_return_t TimeStats::pullGlobalAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie) { status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie) { impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie); if (atom_tag != android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) { if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) { return timeStats->populateGlobalAtom(data); } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) { return timeStats->populateLayerAtom(data); } return STATS_PULL_SKIP; } std::lock_guard<std::mutex> lock(timeStats->mMutex); status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) { std::lock_guard<std::mutex> lock(mMutex); const auto& stats = timeStats->mTimeStats; if (stats.statsStart == 0) { if (mTimeStats.statsStart == 0) { return STATS_PULL_SKIP; } timeStats->flushPowerTimeLocked(); flushPowerTimeLocked(); struct stats_event* event = timeStats->mStatsDelegate->addStatsEventToPullData(data); timeStats->mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.totalFrames); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.missedFrames); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.clientCompositionFrames); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.displayOnTime); timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.presentToPresent.totalTime()); timeStats->mStatsDelegate->statsEventBuild(event); timeStats->clearGlobalLocked(); struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data); mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime); mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime()); mStatsDelegate->statsEventBuild(event); clearGlobalLocked(); return STATS_PULL_SUCCESS; } TimeStats::TimeStats() : TimeStats(nullptr) {} namespace { // Histograms align with the order of fields in SurfaceflingerStatsLayerInfo. const std::array<std::string, 6> kHistogramNames = { "present2present", "post2present", "acquire2present", "latch2present", "desired2present", "post2acquire", }; std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram, size_t maxPulledHistogramBuckets) { auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end()); std::sort(buckets.begin(), buckets.end(), [](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) { return left.second > right.second; }); util::ProtoOutputStream proto; int histogramSize = 0; for (const auto& bucket : buckets) { if (++histogramSize > maxPulledHistogramBuckets) { break; } proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED | 1 /* field id */, (int32_t)bucket.first); proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | 2 /* field id */, (int64_t)bucket.second); } std::string byteString; proto.serializeToString(&byteString); return byteString; } } // namespace status_pull_atom_return_t TimeStats::populateLayerAtom(pulled_stats_event_list* data) { std::lock_guard<std::mutex> lock(mMutex); std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats; for (const auto& ele : mTimeStats.stats) { dumpStats.push_back(&ele.second); } std::sort(dumpStats.begin(), dumpStats.end(), [](TimeStatsHelper::TimeStatsLayer const* l, TimeStatsHelper::TimeStatsLayer const* r) { return l->totalFrames > r->totalFrames; }); TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate) { if (mMaxPulledLayers < dumpStats.size()) { dumpStats.resize(mMaxPulledLayers); } for (const auto& layer : dumpStats) { struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data); mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO); mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str()); mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames); mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames); for (const auto& name : kHistogramNames) { const auto& histogram = layer->deltas.find(name); if (histogram == layer->deltas.cend()) { mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0); } else { std::string bytes = histogramToProtoByteString(histogram->second.hist, mMaxPulledHistogramBuckets); mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(), bytes.size()); } } mStatsDelegate->statsEventBuild(event); } clearLayersLocked(); return STATS_PULL_SUCCESS; } TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {} TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate, std::optional<size_t> maxPulledLayers, std::optional<size_t> maxPulledHistogramBuckets) { if (statsDelegate != nullptr) { mStatsDelegate = std::move(statsDelegate); } if (maxPulledLayers) { mMaxPulledLayers = *maxPulledLayers; } if (maxPulledHistogramBuckets) { mMaxPulledHistogramBuckets = *maxPulledHistogramBuckets; } } void TimeStats::onBootFinished() { Loading Loading @@ -634,7 +727,9 @@ void TimeStats::enable() { mTimeStats.statsStart = static_cast<int64_t>(std::time(0)); mPowerTime.prevTime = systemTime(); mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, TimeStats::pullGlobalAtomCallback, nullptr, this); TimeStats::pullAtomCallback, nullptr, this); mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO, TimeStats::pullAtomCallback, nullptr, this); ALOGD("Enabled"); } Loading @@ -649,6 +744,7 @@ void TimeStats::disable() { mTimeStats.statsEnd = static_cast<int64_t>(std::time(0)); mStatsDelegate->unregisterStatsPullAtomCallback( android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO); ALOGD("Disabled"); } Loading
services/surfaceflinger/TimeStats/TimeStats.h +18 −4 Original line number Diff line number Diff line Loading @@ -165,10 +165,21 @@ public: return stats_event_write_int64(event, field); } virtual void statsEventWriteString8(struct stats_event* event, const char* field) { return stats_event_write_string8(event, field); } virtual void statsEventWriteByteArray(struct stats_event* event, const uint8_t* buf, size_t numBytes) { return stats_event_write_byte_array(event, buf, numBytes); } virtual void statsEventBuild(struct stats_event* event) { return stats_event_build(event); } }; // For testing only for injecting custom dependencies. TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate); TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate, std::optional<size_t> maxPulledLayers, std::optional<size_t> maxPulledHistogramBuckets); void onBootFinished() override; void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override; Loading Loading @@ -207,9 +218,10 @@ public: static const size_t MAX_NUM_TIME_RECORDS = 64; private: static status_pull_atom_return_t pullGlobalAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie); static status_pull_atom_return_t pullAtomCallback(int32_t atom_tag, pulled_stats_event_list* data, void* cookie); status_pull_atom_return_t populateGlobalAtom(pulled_stats_event_list* data); status_pull_atom_return_t populateLayerAtom(pulled_stats_event_list* data); bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord); void flushAvailableRecordsToStatsLocked(int32_t layerId); void flushPowerTimeLocked(); Loading @@ -233,6 +245,8 @@ private: static const size_t MAX_NUM_LAYER_RECORDS = 200; static const size_t MAX_NUM_LAYER_STATS = 200; std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>(); size_t mMaxPulledLayers = 8; size_t mMaxPulledHistogramBuckets = 6; }; } // namespace impl Loading
services/surfaceflinger/tests/unittests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,7 @@ cc_test { "perfetto_trace_protos", ], shared_libs: [ "libprotoutil", "libstatssocket", "libsurfaceflinger", "libtimestats", Loading
services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +279 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes