diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 43058d5385523..6b20aa71fb3c2 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -74,6 +74,7 @@ cc_defaults { "src/external/StatsPuller.cpp", "src/external/StatsPullerManager.cpp", "src/external/SubsystemSleepStatePuller.cpp", + "src/external/SurfaceflingerStatsPuller.cpp", "src/external/TrainInfoPuller.cpp", "src/FieldValue.cpp", "src/guardrail/StatsdStats.cpp", @@ -136,6 +137,7 @@ cc_defaults { "libservices", "libstatslog", "libsysutils", + "libtimestats_proto", "libutils", ], } @@ -237,6 +239,7 @@ cc_test { "tests/external/IncidentReportArgs_test.cpp", "tests/external/puller_util_test.cpp", "tests/external/StatsPuller_test.cpp", + "tests/external/SurfaceflingerStatsPuller_test.cpp", "tests/FieldValue_test.cpp", "tests/guardrail/StatsdStats_test.cpp", "tests/indexed_priority_queue_test.cpp", diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 475f18a9b0b8d..4cf7bc4257166 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -17,12 +17,17 @@ #define DEBUG false #include "Log.h" +#include "StatsPullerManager.h" + #include #include #include #include #include + #include +#include + #include "../StatsService.h" #include "../logd/LogEvent.h" #include "../stats_log_util.h" @@ -32,13 +37,11 @@ #include "ResourceHealthManagerPuller.h" #include "StatsCallbackPuller.h" #include "StatsCompanionServicePuller.h" -#include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" +#include "SurfaceflingerStatsPuller.h" #include "TrainInfoPuller.h" #include "statslog.h" -#include - using std::make_shared; using std::map; using std::shared_ptr; @@ -266,6 +269,10 @@ std::map StatsPullerManager::kAllPullAtomInfo = { // App ops {android::util::APP_OPS, {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}}, + // SurfaceflingerStatsGlobalInfo + {android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, + {.puller = + new SurfaceflingerStatsPuller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp new file mode 100644 index 0000000000000..23b2236f35f2d --- /dev/null +++ b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SurfaceflingerStatsPuller.h" + +#include + +#include + +#include "logd/LogEvent.h" +#include "stats_log_util.h" +#include "statslog.h" + +namespace android { +namespace os { +namespace statsd { + +SurfaceflingerStatsPuller::SurfaceflingerStatsPuller(const int tagId) : StatsPuller(tagId) { +} + +bool SurfaceflingerStatsPuller::PullInternal(std::vector>* data) { + switch (mTagId) { + case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO: + return pullGlobalInfo(data); + default: + break; + } + + return false; +} + +static int64_t getTotalTime( + const google::protobuf::RepeatedPtrField& + buckets) { + int64_t total = 0; + for (const auto& bucket : buckets) { + if (bucket.time_millis() == 1000) { + continue; + } + + total += bucket.time_millis() * bucket.frame_count(); + } + + return total; +} + +bool SurfaceflingerStatsPuller::pullGlobalInfo(std::vector>* data) { + std::string protoBytes; + if (CC_UNLIKELY(mStatsProvider)) { + protoBytes = mStatsProvider(); + } else { + std::unique_ptr pipe(popen("dumpsys SurfaceFlinger --timestats -dump --proto", "r"), pclose); + if (!pipe.get()) { + return false; + } + char buf[1024]; + size_t bytesRead = 0; + do { + bytesRead = fread(buf, 1, sizeof(buf), pipe.get()); + protoBytes.append(buf, bytesRead); + } while (bytesRead > 0); + } + surfaceflinger::SFTimeStatsGlobalProto proto; + proto.ParseFromString(protoBytes); + + int64_t totalTime = getTotalTime(proto.present_to_present()); + + data->clear(); + data->reserve(1); + std::shared_ptr event = + make_shared(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, getWallClockNs(), + getElapsedRealtimeNs()); + if (!event->write(proto.total_frames())) return false; + if (!event->write(proto.missed_frames())) return false; + if (!event->write(proto.client_composition_frames())) return false; + if (!event->write(proto.display_on_time())) return false; + if (!event->write(totalTime)) return false; + event->init(); + data->emplace_back(event); + + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h new file mode 100644 index 0000000000000..ed7153edf797e --- /dev/null +++ b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Pull metrics from Surfaceflinger + */ +class SurfaceflingerStatsPuller : public StatsPuller { +public: + explicit SurfaceflingerStatsPuller(const int tagId); + + // StatsPuller interface + bool PullInternal(std::vector>* data) override; + +protected: + // Test-only, for injecting fake data + using StatsProvider = std::function; + StatsProvider mStatsProvider; + +private: + bool pullGlobalInfo(std::vector>* data); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp new file mode 100644 index 0000000000000..5c9636fa99cc5 --- /dev/null +++ b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "SurfaceflingerStatsPuller_test" + +#include "src/external/SurfaceflingerStatsPuller.h" + +#include +#include + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +class TestableSurfaceflingerStatsPuller : public SurfaceflingerStatsPuller { +public: + TestableSurfaceflingerStatsPuller(const int tagId) : SurfaceflingerStatsPuller(tagId){}; + + void injectStats(const StatsProvider& statsProvider) { + mStatsProvider = statsProvider; + } +}; + +class SurfaceflingerStatsPullerTest : public ::testing::Test { +public: + SurfaceflingerStatsPullerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~SurfaceflingerStatsPullerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } +}; + +TEST_F(SurfaceflingerStatsPullerTest, pullGlobalStats) { + surfaceflinger::SFTimeStatsGlobalProto proto; + proto.set_total_frames(1); + proto.set_missed_frames(2); + proto.set_client_composition_frames(2); + proto.set_display_on_time(4); + + auto bucketOne = proto.add_present_to_present(); + bucketOne->set_time_millis(2); + bucketOne->set_frame_count(4); + auto bucketTwo = proto.add_present_to_present(); + bucketTwo->set_time_millis(4); + bucketTwo->set_frame_count(1); + auto bucketThree = proto.add_present_to_present(); + bucketThree->set_time_millis(1000); + bucketThree->set_frame_count(1); + static constexpr int64_t expectedAnimationMillis = 12; + TestableSurfaceflingerStatsPuller puller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); + + puller.injectStats([&] { + return proto.SerializeAsString(); + }); + puller.ForceClearCache(); + vector> outData; + puller.Pull(&outData); + + ASSERT_EQ(1, outData.size()); + EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, outData[0]->GetTagId()); + EXPECT_EQ(proto.total_frames(), outData[0]->getValues()[0].mValue.long_value); + EXPECT_EQ(proto.missed_frames(), outData[0]->getValues()[1].mValue.long_value); + EXPECT_EQ(proto.client_composition_frames(), outData[0]->getValues()[2].mValue.long_value); + EXPECT_EQ(proto.display_on_time(), outData[0]->getValues()[3].mValue.long_value); + EXPECT_EQ(expectedAnimationMillis, outData[0]->getValues()[4].mValue.long_value); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif