[TimeStats] Add callback for layer stats
Bug: 119885568
Test: adb shell cmd stats pull-source 10063
Change-Id: I385c9a49e2c7f98d4dacba6ac399e927c5b0e192
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 7ff2594..d27fbb4 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -8,6 +8,7 @@
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
+ "libprotoutil",
"libstatslog",
"libstatspull",
"libstatssocket",
@@ -17,6 +18,7 @@
],
export_include_dirs: ["."],
export_shared_lib_headers: [
+ "libprotoutil",
"libstatslog",
"libstatspull",
"libstatssocket",
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 44a59fd..7c824ec 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -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>
@@ -36,42 +37,134 @@
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) {
- return STATS_PULL_SKIP;
+ 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);
}
- std::lock_guard<std::mutex> lock(timeStats->mMutex);
+ return STATS_PULL_SKIP;
+}
- const auto& stats = timeStats->mTimeStats;
- if (stats.statsStart == 0) {
+status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ 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",
+};
-TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate) {
+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;
+ });
+
+ 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() {
@@ -634,7 +727,9 @@
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");
}
@@ -649,6 +744,7 @@
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");
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5cd421c..cf1c3c6 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -165,10 +165,21 @@
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;
@@ -207,9 +218,10 @@
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();
@@ -233,6 +245,8 @@
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
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index e500672..1eaf2dd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -83,6 +83,7 @@
"perfetto_trace_protos",
],
shared_libs: [
+ "libprotoutil",
"libstatssocket",
"libsurfaceflinger",
"libtimestats",
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index c667080..5044626 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -22,6 +22,7 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <TimeStats/TimeStats.h>
+#include <android/util/ProtoOutputStream.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -41,9 +42,11 @@
namespace {
using testing::_;
+using testing::AnyNumber;
using testing::Contains;
using testing::InSequence;
using testing::SizeIs;
+using testing::StrEq;
using testing::UnorderedElementsAre;
// clang-format off
@@ -149,7 +152,7 @@
}
void registerStatsPullAtomCallback(int32_t atom_tag, stats_pull_atom_callback_t callback,
pull_atom_metadata*, void* cookie) override {
- mAtomTag = atom_tag;
+ mAtomTags.push_back(atom_tag);
mCallback = callback;
mCookie = cookie;
}
@@ -161,17 +164,19 @@
MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
MOCK_METHOD2(statsEventSetAtomId, void(struct stats_event*, uint32_t));
MOCK_METHOD2(statsEventWriteInt64, void(struct stats_event*, int64_t));
+ MOCK_METHOD2(statsEventWriteString8, void(struct stats_event*, const char*));
+ MOCK_METHOD3(statsEventWriteByteArray, void(struct stats_event*, const uint8_t*, size_t));
MOCK_METHOD1(statsEventBuild, void(struct stats_event*));
struct stats_event* mEvent = stats_event_obtain();
- int32_t mAtomTag = 0;
+ std::vector<int32_t> mAtomTags;
stats_pull_atom_callback_t mCallback = nullptr;
void* mCookie = nullptr;
};
-
FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
std::unique_ptr<TimeStats> mTimeStats =
- std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate));
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+ std::nullopt, std::nullopt);
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -260,10 +265,14 @@
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
- EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, mDelegate->mAtomTag);
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_CALL(*mDelegate,
unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
ASSERT_FALSE(mTimeStats->isEnabled());
}
@@ -696,7 +705,9 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, mDelegate->mAtomTag);
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_NE(nullptr, mDelegate->mCallback);
EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
@@ -713,7 +724,8 @@
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
EXPECT_EQ(STATS_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(mDelegate->mAtomTag, mDelegate->mCookie));
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ mDelegate->mCookie));
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -724,6 +736,266 @@
EXPECT_EQ(0, globalProto.present_to_present_size());
}
+namespace {
+std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
+ const std::vector<int32_t>& frameCounts) {
+ util::ProtoOutputStream proto;
+ for (int i = 0; i < times.size(); i++) {
+ ALOGE("Writing time: %d", times[i]);
+ proto.write(util::FIELD_TYPE_INT32 | util::FIELD_COUNT_REPEATED | 1 /* field id */,
+ (int32_t)times[i]);
+ ALOGE("Writing count: %d", frameCounts[i]);
+ proto.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED | 2 /* field id */,
+ (int64_t)frameCounts[i]);
+ }
+ std::string byteString;
+ proto.serializeToString(&byteString);
+ return byteString;
+}
+
+std::string dumpByteStringHex(const std::string& str) {
+ std::stringstream ss;
+ ss << std::hex;
+ for (const char& c : str) {
+ ss << (int)c << " ";
+ }
+
+ return ss.str();
+}
+
+} // namespace
+
+MATCHER_P2(BytesEq, bytes, size, "") {
+ std::string expected;
+ expected.append((const char*)bytes, size);
+ std::string actual;
+ actual.append((const char*)arg, size);
+
+ *result_listener << "Bytes are not equal! \n";
+ *result_listener << "size: " << size << "\n";
+ *result_listener << "expected: " << dumpByteStringHex(expected).c_str() << "\n";
+ *result_listener << "actual: " << dumpByteStringHex(actual).c_str() << "\n";
+
+ return expected == actual;
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsAllHistogramsAndClears) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {1});
+ std::string expectedPostToPresent = buildExpectedHistogramBytestring({4}, {1});
+ std::string expectedAcquireToPresent = buildExpectedHistogramBytestring({3}, {1});
+ std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
+ std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
+ std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent,
+ StrEq(genLayerName(LAYER_ID_0).c_str())));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 0));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedPresentToPresent.c_str(),
+ expectedPresentToPresent.size()),
+ expectedPresentToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)expectedPostToPresent.c_str(),
+ expectedPostToPresent.size()),
+ expectedPostToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedAcquireToPresent.c_str(),
+ expectedAcquireToPresent.size()),
+ expectedAcquireToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)expectedLatchToPresent.c_str(),
+ expectedLatchToPresent.size()),
+ expectedLatchToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedDesiredToPresent.c_str(),
+ expectedDesiredToPresent.size()),
+ expectedDesiredToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
+ expectedPostToAcquire.size()),
+ expectedPostToAcquire.size()));
+ EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+ .Times(2);
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+ // Now make sure that TimeStats flushes global stats to register the
+ // callback.
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1, 2}, {2, 1});
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedPresentToPresent.c_str(),
+ expectedPresentToPresent.size()),
+ expectedPresentToPresent.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+ .Times(AnyNumber());
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
+ mDelegate = new FakeStatsEventDelegate;
+ mTimeStats =
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+ std::nullopt, 1);
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {2});
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedPresentToPresent.c_str(),
+ expectedPresentToPresent.size()),
+ expectedPresentToPresent.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+ .Times(AnyNumber());
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
+ mDelegate = new FakeStatsEventDelegate;
+ mTimeStats =
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
+ std::nullopt);
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+ .Times(1);
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
TEST_F(TimeStatsTest, canSurviveMonkey) {
if (g_noSlowTests) {
GTEST_SKIP();