summaryrefslogtreecommitdiff
path: root/services/surfaceflinger
diff options
context:
space:
mode:
authorAlec Mouri <alecmouri@google.com>2019-12-07 18:18:48 -0800
committerAlec Mouri <alecmouri@google.com>2020-01-15 19:05:44 -0800
commitd5065451af373b954ee660d26d0fbf7b68f4c539 (patch)
treec5b33117b955179acc915d902906b82b7a14e449 /services/surfaceflinger
parent0f67b9fa6cae94800ba9d8bbfd5265ae250e8621 (diff)
downloadnative-d5065451af373b954ee660d26d0fbf7b68f4c539.tar.gz
[TimeStats] Add callback for global stats
Now that the new statsd api is completed, we can implement the puller logic in TimeStats instead of in statsd's code. Bug: 119885568 Test: adb shell cmd stats pull-source 10062 Change-Id: Ic90ec5ddc082160bd7e784becf8bf3c3a99ef971
Diffstat (limited to 'services/surfaceflinger')
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp1
-rw-r--r--services/surfaceflinger/TimeStats/Android.bp8
-rw-r--r--services/surfaceflinger/TimeStats/TimeStats.cpp91
-rw-r--r--services/surfaceflinger/TimeStats/TimeStats.h53
-rw-r--r--services/surfaceflinger/tests/unittests/Android.bp1
-rw-r--r--services/surfaceflinger/tests/unittests/TimeStatsTest.cpp98
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockTimeStats.h1
7 files changed, 243 insertions, 10 deletions
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b7a2c760e6..97bed34b93 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -490,6 +490,7 @@ void SurfaceFlinger::bootFinished()
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mFrameTracer->initialize();
+ mTimeStats->onBootFinished();
// wait patiently for the window manager death
const String16 name("window");
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 20c22184d2..ed0fe9e000 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -5,15 +5,23 @@ cc_library_shared {
],
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libtimestats_proto",
"libui",
"libutils",
],
export_include_dirs: ["."],
export_shared_lib_headers: [
+ "libbinder",
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libtimestats_proto",
],
cppflags: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index a5fabf2c16..6520d011bb 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,14 +32,54 @@ namespace android {
namespace impl {
+bool TimeStats::pullGlobalAtomCallback(int32_t atom_tag, pulled_stats_event_list* data,
+ const void* cookie) {
+ impl::TimeStats* timeStats =
+ const_cast<impl::TimeStats*>(reinterpret_cast<const impl::TimeStats*>(cookie));
+ if (atom_tag != android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> lock(timeStats->mMutex);
+
+ const auto& stats = timeStats->mTimeStats;
+ if (stats.statsStart == 0) {
+ return false;
+ }
+ timeStats->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();
+
+ return true;
+}
+
TimeStats::TimeStats() {
// Temporarily enable TimeStats by default. Telemetry is disabled while
// we move onto statsd, so TimeStats is currently not exercised at all
- // during testing.
- // TODO: remove this.
+ // during testing without enabling by default.
+ // TODO: remove this, as we should only be paying this overhead on devices
+ // where statsd exists.
enable();
}
+TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate) : TimeStats() {
+ mStatsDelegate = std::move(statsDelegate);
+}
+
+void TimeStats::onBootFinished() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRegisteredCallback = false;
+}
+
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
ATRACE_CALL();
@@ -65,7 +105,7 @@ void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::strin
}
if (argsMap.count("-clear")) {
- clear();
+ clearAll();
}
if (argsMap.count("-enable")) {
@@ -549,6 +589,30 @@ void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
mGlobalRecord.renderEngineDurations.pop_front();
}
+ // Try to register to statsd at the end of every global flush, if we haven't
+ // yet.
+ registerToStatsdIfNeededLocked();
+}
+
+bool TimeStats::StatsEventDelegate::checkStatsService() {
+ ATRACE_CALL();
+ bool ret =
+ android::defaultServiceManager()->checkService(android::String16("stats")) != nullptr;
+ return ret;
+}
+
+void TimeStats::registerToStatsdIfNeededLocked() {
+ if (!mRegisteredCallback && mStatsDelegate->checkStatsService()) {
+ // Note that this assumes that statsd will never crash. To be absolutely
+ // correct we would need to register a DeathRecipient ourselves, but to
+ // minimize the cost on the rendering path let's only register once as
+ // soon as we know that statd has booted up.
+ ALOGD("Registering statsd callback");
+ mStatsDelegate
+ ->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ TimeStats::pullGlobalAtomCallback, nullptr, this);
+ mRegisteredCallback = true;
+ }
}
void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
@@ -605,12 +669,15 @@ void TimeStats::disable() {
ALOGD("Disabled");
}
-void TimeStats::clear() {
+void TimeStats::clearAll() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ clearGlobalLocked();
+ clearLayersLocked();
+}
+
+void TimeStats::clearGlobalLocked() {
ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mMutex);
- mTimeStatsTracker.clear();
- mTimeStats.stats.clear();
mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
mTimeStats.statsEnd = 0;
mTimeStats.totalFrames = 0;
@@ -624,7 +691,15 @@ void TimeStats::clear() {
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
mGlobalRecord.presentFences.clear();
- ALOGD("Cleared");
+ ALOGD("Cleared global stats");
+}
+
+void TimeStats::clearLayersLocked() {
+ ATRACE_CALL();
+
+ mTimeStatsTracker.clear();
+ mTimeStats.stats.clear();
+ ALOGD("Cleared layer stats");
}
bool TimeStats::isEnabled() {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 65e5cf4419..94c24ea4a8 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -16,7 +16,11 @@
#pragma once
+#include <binder/IServiceManager.h>
#include <hardware/hwcomposer_defs.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
@@ -37,6 +41,10 @@ class TimeStats {
public:
virtual ~TimeStats() = default;
+ // Called once boot has been finished to perform additional capabilities,
+ // e.g. registration to statsd.
+ virtual void onBootFinished() = 0;
+
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
virtual std::string miniDump() = 0;
@@ -131,6 +139,40 @@ class TimeStats : public android::TimeStats {
public:
TimeStats();
+ // Delegate to the statsd service and associated APIs.
+ // Production code may use this class directly, whereas unit test may define
+ // a subclass for ease of testing.
+ class StatsEventDelegate {
+ public:
+ virtual ~StatsEventDelegate() = default;
+ virtual struct stats_event* addStatsEventToPullData(pulled_stats_event_list* data) {
+ return add_stats_event_to_pull_data(data);
+ }
+ virtual void registerStatsPullAtomCallback(int32_t atom_tag,
+ stats_pull_atom_callback_t callback,
+ pull_atom_metadata* metadata, void* cookie) {
+ return register_stats_pull_atom_callback(atom_tag, callback, metadata, cookie);
+ }
+
+ // Check if the statsd daemon exists, as otherwise callback registration
+ // will silently fail.
+ virtual bool checkStatsService();
+
+ virtual void statsEventSetAtomId(struct stats_event* event, int32_t atom_id) {
+ return stats_event_set_atom_id(event, atom_id);
+ }
+
+ virtual void statsEventWriteInt64(struct stats_event* event, int64_t field) {
+ return stats_event_write_int64(event, field);
+ }
+
+ 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);
+
+ void onBootFinished() override;
+
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -167,14 +209,19 @@ public:
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
+ static bool pullGlobalAtomCallback(int32_t atom_tag, pulled_stats_event_list* data,
+ const void* cookie);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
+ void registerToStatsdIfNeededLocked();
void enable();
void disable();
- void clear();
+ void clearAll();
+ void clearGlobalLocked();
+ void clearLayersLocked();
void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
std::atomic<bool> mEnabled = false;
@@ -187,6 +234,10 @@ private:
static const size_t MAX_NUM_LAYER_RECORDS = 200;
static const size_t MAX_NUM_LAYER_STATS = 200;
+ // Default is true, so that registration doesn't happen until the device has
+ // been booted.
+ bool mRegisteredCallback = true;
+ std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
};
} // namespace impl
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index e4ef19ea7a..b43e939005 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -82,6 +82,7 @@ cc_test {
"perfetto_trace_protos",
],
shared_libs: [
+ "libstatssocket",
"libsurfaceflinger",
"libtimestats",
"libtimestats_proto",
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 3e808c0abb..ec27201bca 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -36,7 +36,9 @@ using namespace google::protobuf;
namespace android {
namespace {
+using testing::_;
using testing::Contains;
+using testing::InSequence;
using testing::SizeIs;
using testing::UnorderedElementsAre;
@@ -132,7 +134,41 @@ public:
}
std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
- std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
+
+ class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
+ public:
+ FakeStatsEventDelegate() = default;
+ ~FakeStatsEventDelegate() override = default;
+
+ struct stats_event* addStatsEventToPullData(pulled_stats_event_list*) override {
+ return mEvent;
+ }
+ void registerStatsPullAtomCallback(int32_t atom_tag, stats_pull_atom_callback_t callback,
+ pull_atom_metadata*, void* cookie) override {
+ mAtomTag = atom_tag;
+ mCallback = callback;
+ mCookie = cookie;
+ }
+
+ bool checkStatsService() override { return true; }
+
+ bool makePullAtomCallback(int32_t atom_tag, const void* cookie) {
+ return (*mCallback)(atom_tag, nullptr, cookie);
+ }
+
+ MOCK_METHOD2(statsEventSetAtomId, void(struct stats_event*, int32_t));
+ MOCK_METHOD2(statsEventWriteInt64, void(struct stats_event*, int64_t));
+ MOCK_METHOD1(statsEventBuild, void(struct stats_event*));
+
+ struct stats_event* mEvent = stats_event_obtain();
+ int32_t mAtomTag = 0;
+ 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::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -627,6 +663,66 @@ TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) {
ASSERT_EQ(0, globalProto.stats_size());
}
+TEST_F(TimeStatsTest, globalStatsCallback_disabledBeforeBoot) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+
+ // Callback should not be registered after flushing global stats
+ EXPECT_EQ(nullptr, mDelegate->mCallback);
+}
+
+TEST_F(TimeStatsTest, globalStatsCallback_worksAfterBoot) {
+ constexpr size_t TOTAL_FRAMES = 5;
+ constexpr size_t MISSED_FRAMES = 4;
+ constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ mTimeStats->onBootFinished();
+
+ for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+ mTimeStats->incrementTotalFrames();
+ }
+ for (size_t i = 0; i < MISSED_FRAMES; i++) {
+ mTimeStats->incrementMissedFrames();
+ }
+ for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+ mTimeStats->incrementClientCompositionFrames();
+ }
+
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ 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_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
+ EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+ }
+ mDelegate->makePullAtomCallback(mDelegate->mAtomTag, mDelegate->mCookie);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.total_frames());
+ EXPECT_EQ(0, globalProto.missed_frames());
+ EXPECT_EQ(0, globalProto.client_composition_frames());
+ EXPECT_EQ(0, globalProto.present_to_present_size());
+}
+
TEST_F(TimeStatsTest, canSurviveMonkey) {
if (g_noSlowTests) {
GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ec74a42f42..9ada5ef4aa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -28,6 +28,7 @@ public:
TimeStats();
~TimeStats() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());