1 /*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "SurfaceFlingerPuller"
18
19 #include "SurfaceFlingerPuller.h"
20
21 #include <gui/SurfaceComposerClient.h>
22 #include <log/log.h>
23 #include <statslog.h>
24 #include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
25
26 #include <vector>
27
28 namespace android {
29 namespace server {
30 namespace stats {
31
32 using android::util::BytesField;
33 using std::optional;
34
35 namespace {
getBytes(const google::protobuf::MessageLite & proto,std::string & data)36 optional<BytesField> getBytes(const google::protobuf::MessageLite& proto, std::string& data) {
37 if (!proto.SerializeToString(&data)) {
38 ALOGW("Unable to serialize surface flinger bytes field");
39 return std::nullopt;
40 }
41 return {BytesField(data.data(), data.size())};
42 }
43 } // namespace
44
pull(int32_t atomTag,AStatsEventList * data)45 AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::pull(int32_t atomTag,
46 AStatsEventList* data) {
47 // Don't need mutexes here, since there is no global state.
48 // SurfaceComposerClient is thread safe, and surfaceflinger is internally thread safe.
49
50 bool success = false;
51 std::string pullDataProto;
52 status_t err = SurfaceComposerClient::onPullAtom(atomTag, &pullDataProto, &success);
53 if (!success || err != NO_ERROR) {
54 ALOGW("Failed to pull atom %" PRId32
55 " from surfaceflinger. Success is %d, binder status is %s",
56 atomTag, (int)success, binder::Status::exceptionToString(err).c_str());
57 return AStatsManager_PULL_SKIP;
58 }
59
60 switch (atomTag) {
61 case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO:
62 return parseGlobalInfoPull(pullDataProto, data);
63 case android::util::SURFACEFLINGER_STATS_LAYER_INFO:
64 return parseLayerInfoPull(pullDataProto, data);
65 default:
66 ALOGW("Invalid atom id for surfaceflinger pullers: %" PRId32, atomTag);
67 return AStatsManager_PULL_SKIP;
68 }
69 }
70
parseGlobalInfoPull(const std::string & protoData,AStatsEventList * data)71 AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::parseGlobalInfoPull(
72 const std::string& protoData, AStatsEventList* data) {
73 android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
74 if (!atomList.ParseFromString(protoData)) {
75 ALOGW("Error parsing surface flinger global stats to proto");
76 return AStatsManager_PULL_SKIP;
77 }
78
79 for (const auto& atom : atomList.atom()) {
80 // The strings must outlive the BytesFields, which only have a pointer to the data.
81 std::string frameDurationStr, renderEngineTimeStr, deadlineMissesStr, predictionErrorsStr;
82 optional<BytesField> frameDuration = getBytes(atom.frame_duration(), frameDurationStr);
83 optional<BytesField> renderEngineTime =
84 getBytes(atom.render_engine_timing(), renderEngineTimeStr);
85 optional<BytesField> deadlineMisses =
86 getBytes(atom.sf_deadline_misses(), deadlineMissesStr);
87 optional<BytesField> predictionErrors =
88 getBytes(atom.sf_prediction_errors(), predictionErrorsStr);
89
90 // Fail if any serialization to bytes failed.
91 if (!frameDuration || !renderEngineTime || !deadlineMisses || !predictionErrors) {
92 return AStatsManager_PULL_SKIP;
93 }
94
95 android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
96 atom.total_frames(), atom.missed_frames(),
97 atom.client_composition_frames(), atom.display_on_millis(),
98 atom.animation_millis(), atom.event_connection_count(),
99 frameDuration.value(), renderEngineTime.value(),
100 atom.total_timeline_frames(), atom.total_janky_frames(),
101 atom.total_janky_frames_with_long_cpu(),
102 atom.total_janky_frames_with_long_gpu(),
103 atom.total_janky_frames_sf_unattributed(),
104 atom.total_janky_frames_app_unattributed(),
105 atom.total_janky_frames_sf_scheduling(),
106 atom.total_jank_frames_sf_prediction_error(),
107 atom.total_jank_frames_app_buffer_stuffing(),
108 atom.display_refresh_rate_bucket(), deadlineMisses.value(),
109 predictionErrors.value(), atom.render_rate_bucket());
110 }
111 return AStatsManager_PULL_SUCCESS;
112 }
113
parseLayerInfoPull(const std::string & protoData,AStatsEventList * data)114 AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::parseLayerInfoPull(
115 const std::string& protoData, AStatsEventList* data) {
116 android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
117 if (!atomList.ParseFromString(protoData)) {
118 ALOGW("Error parsing surface flinger layer stats to proto");
119 return AStatsManager_PULL_SKIP;
120 }
121
122 for (const auto& atom : atomList.atom()) {
123 // The strings must outlive the BytesFields, which only have a pointer to the data.
124 std::string present2PresentStr, post2presentStr, acquire2PresentStr, latch2PresentStr,
125 desired2PresentStr, post2AcquireStr, frameRateVoteStr, appDeadlineMissesStr;
126 optional<BytesField> present2Present =
127 getBytes(atom.present_to_present(), present2PresentStr);
128 optional<BytesField> post2present = getBytes(atom.post_to_present(), post2presentStr);
129 optional<BytesField> acquire2Present =
130 getBytes(atom.acquire_to_present(), acquire2PresentStr);
131 optional<BytesField> latch2Present = getBytes(atom.latch_to_present(), latch2PresentStr);
132 optional<BytesField> desired2Present =
133 getBytes(atom.desired_to_present(), desired2PresentStr);
134 optional<BytesField> post2Acquire = getBytes(atom.post_to_acquire(), post2AcquireStr);
135 optional<BytesField> frameRateVote = getBytes(atom.set_frame_rate_vote(), frameRateVoteStr);
136 optional<BytesField> appDeadlineMisses =
137 getBytes(atom.app_deadline_misses(), appDeadlineMissesStr);
138
139 // Fail if any serialization to bytes failed.
140 if (!present2Present || !post2present || !acquire2Present || !latch2Present ||
141 !desired2Present || !post2Acquire || !frameRateVote || !appDeadlineMisses) {
142 return AStatsManager_PULL_SKIP;
143 }
144
145 android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_LAYER_INFO,
146 atom.layer_name().c_str(), atom.total_frames(),
147 atom.dropped_frames(), present2Present.value(),
148 post2present.value(), acquire2Present.value(),
149 latch2Present.value(), desired2Present.value(),
150 post2Acquire.value(), atom.late_acquire_frames(),
151 atom.bad_desired_present_frames(), atom.uid(),
152 atom.total_timeline_frames(), atom.total_janky_frames(),
153 atom.total_janky_frames_with_long_cpu(),
154 atom.total_janky_frames_with_long_gpu(),
155 atom.total_janky_frames_sf_unattributed(),
156 atom.total_janky_frames_app_unattributed(),
157 atom.total_janky_frames_sf_scheduling(),
158 atom.total_jank_frames_sf_prediction_error(),
159 atom.total_jank_frames_app_buffer_stuffing(),
160 atom.display_refresh_rate_bucket(), atom.render_rate_bucket(),
161 frameRateVote.value(), appDeadlineMisses.value(),
162 atom.game_mode());
163 }
164 return AStatsManager_PULL_SUCCESS;
165 }
166
167 } // namespace stats
168 } // namespace server
169 } // namespace android
170