From 746568d25b2075985eba51d9f14d86912dee9281 Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Sat, 10 Apr 2021 02:55:55 -0700 Subject: [PATCH] Reroute surfaceflinger atoms through system server System server changes for routing surfaceflinger pulled atoms through system server. Pullers are registered in StatsPullAtomService. All logic is done in JNI since SurfaceComposerClient is only available in native. The JNI structure is modeled off of ag/10209693. Test: statsd_testdrive 10062 10063 Bug: 184698814 Change-Id: I55c75c485f49f75367cc285a84f12f316139241b --- .../stats/pull/StatsPullAtomService.java | 4 +- services/core/jni/Android.bp | 3 + ...server_stats_pull_StatsPullAtomService.cpp | 61 +++++++ services/core/jni/onload.cpp | 2 + services/core/jni/stats/OWNERS | 8 + .../core/jni/stats/SurfaceFlingerPuller.cpp | 168 ++++++++++++++++++ .../core/jni/stats/SurfaceFlingerPuller.h | 44 +++++ 7 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp create mode 100644 services/core/jni/stats/OWNERS create mode 100644 services/core/jni/stats/SurfaceFlingerPuller.cpp create mode 100644 services/core/jni/stats/SurfaceFlingerPuller.h diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index a7e2d1dcceec7..a82c91e2352b3 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 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. @@ -432,6 +432,7 @@ public class StatsPullAtomService extends SystemService { mContext = context; } + private native void initializeNativePullers(); /** * Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would * get if we used lambdas. @@ -713,6 +714,7 @@ public class StatsPullAtomService extends SystemService { super.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { BackgroundThread.getHandler().post(() -> { + initializeNativePullers(); // Initialize pullers that need JNI. initializePullersState(); registerPullers(); registerEventListeners(); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index a99679ade9585..8621ab86ba5ad 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -34,6 +34,7 @@ cc_library_static { "gnss/GnssMeasurement.cpp", "gnss/GnssMeasurementCallback.cpp", "gnss/Utils.cpp", + "stats/SurfaceFlingerPuller.cpp", "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", @@ -53,6 +54,7 @@ cc_library_static { "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", + "com_android_server_stats_pull_StatsPullAtomService.cpp", "com_android_server_storage_AppFuseBridge.cpp", "com_android_server_SystemServer.cpp", "com_android_server_tv_TvUinputBridge.cpp", @@ -127,6 +129,7 @@ cc_defaults { "libsensorservice", "libsensorservicehidl", "libgui", + "libtimestats_atoms_proto", "libusbhost", "libtinyalsa", "libEGL", diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp new file mode 100644 index 0000000000000..46fe595d49af7 --- /dev/null +++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2020 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. + */ + +#define LOG_TAG "StatsPullAtomService" + +#include +#include +#include +#include +#include +#include + +#include "stats/SurfaceFlingerPuller.h" + +namespace android { + +static server::stats::SurfaceFlingerPuller gSurfaceFlingerPuller; + +static AStatsManager_PullAtomCallbackReturn onSurfaceFlingerPullCallback(int32_t atom_tag, + AStatsEventList* data, + void* cookie) { + return gSurfaceFlingerPuller.pull(atom_tag, data); +} + +static void initializeNativePullers(JNIEnv* env, jobject javaObject) { + // Surface flinger layer & global info. + gSurfaceFlingerPuller = server::stats::SurfaceFlingerPuller(); + AStatsManager_setPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, + /* metadata= */ nullptr, onSurfaceFlingerPullCallback, + /* cookie= */ nullptr); + AStatsManager_setPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO, + /* metadata= */ nullptr, onSurfaceFlingerPullCallback, + /* cookie= */ nullptr); +} + +static const JNINativeMethod sMethods[] = { + {"initializeNativePullers", "()V", (void*)initializeNativePullers}}; + +int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/stats/pull/StatsPullAtomService", + sMethods, NELEM(sMethods)); + if (res < 0) { + ALOGE("failed to register native methods"); + } + return res; +} + +} // namespace android \ No newline at end of file diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index f257686cbf3d3..b043e643b2bd0 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -61,6 +61,7 @@ int register_android_server_com_android_server_pm_PackageManagerShellCommandData int register_android_server_AdbDebuggingManager(JNIEnv* env); int register_android_server_FaceService(JNIEnv* env); int register_android_server_GpuService(JNIEnv* env); +int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); }; using namespace android; @@ -115,5 +116,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_AdbDebuggingManager(env); register_android_server_FaceService(env); register_android_server_GpuService(env); + register_android_server_stats_pull_StatsPullAtomService(env); return JNI_VERSION_1_4; } diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS new file mode 100644 index 0000000000000..2611e5b6cee2e --- /dev/null +++ b/services/core/jni/stats/OWNERS @@ -0,0 +1,8 @@ +jeffreyhuang@google.com +jtnguyen@google.com +muhammadq@google.com +sharaieko@google.com +singhtejinder@google.com +tsaichristine@google.com +yaochen@google.com +yro@google.com diff --git a/services/core/jni/stats/SurfaceFlingerPuller.cpp b/services/core/jni/stats/SurfaceFlingerPuller.cpp new file mode 100644 index 0000000000000..0e28da764ea79 --- /dev/null +++ b/services/core/jni/stats/SurfaceFlingerPuller.cpp @@ -0,0 +1,168 @@ +/* + * Copyright 2021 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. + */ + +#define LOG_TAG "SurfaceFlingerPuller" + +#include "SurfaceFlingerPuller.h" + +#include +#include +#include +#include + +#include + +namespace android { +namespace server { +namespace stats { + +using android::util::BytesField; +using std::optional; + +namespace { +optional getBytes(const google::protobuf::MessageLite& proto, std::string& data) { + if (!proto.SerializeToString(&data)) { + ALOGW("Unable to serialize surface flinger bytes field"); + return std::nullopt; + } + return {BytesField(data.data(), data.size())}; +} +} // namespace + +AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::pull(int32_t atomTag, + AStatsEventList* data) { + // Don't need mutexes here, since there is no global state. + // SurfaceComposerClient is thread safe, and surfaceflinger is internally thread safe. + + bool success = false; + std::string pullDataProto; + status_t err = SurfaceComposerClient::onPullAtom(atomTag, &pullDataProto, &success); + if (!success || err != NO_ERROR) { + ALOGW("Failed to pull atom %" PRId32 + " from surfaceflinger. Success is %d, binder status is %s", + atomTag, (int)success, binder::Status::exceptionToString(err).c_str()); + return AStatsManager_PULL_SKIP; + } + + switch (atomTag) { + case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO: + return parseGlobalInfoPull(pullDataProto, data); + case android::util::SURFACEFLINGER_STATS_LAYER_INFO: + return parseLayerInfoPull(pullDataProto, data); + default: + ALOGW("Invalid atom id for surfaceflinger pullers: %" PRId32, atomTag); + return AStatsManager_PULL_SKIP; + } +} + +AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::parseGlobalInfoPull( + const std::string& protoData, AStatsEventList* data) { + android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList; + if (!atomList.ParseFromString(protoData)) { + ALOGW("Error parsing surface flinger global stats to proto"); + return AStatsManager_PULL_SKIP; + } + + for (const auto& atom : atomList.atom()) { + // The strings must outlive the BytesFields, which only have a pointer to the data. + std::string frameDurationStr, renderEngineTimeStr, deadlineMissesStr, predictionErrorsStr; + optional frameDuration = getBytes(atom.frame_duration(), frameDurationStr); + optional renderEngineTime = + getBytes(atom.render_engine_timing(), renderEngineTimeStr); + optional deadlineMisses = + getBytes(atom.sf_deadline_misses(), deadlineMissesStr); + optional predictionErrors = + getBytes(atom.sf_prediction_errors(), predictionErrorsStr); + + // Fail if any serialization to bytes failed. + if (!frameDuration || !renderEngineTime || !deadlineMisses || !predictionErrors) { + return AStatsManager_PULL_SKIP; + } + + android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, + atom.total_frames(), atom.missed_frames(), + atom.client_composition_frames(), atom.display_on_millis(), + atom.animation_millis(), atom.event_connection_count(), + frameDuration.value(), renderEngineTime.value(), + atom.total_timeline_frames(), atom.total_janky_frames(), + atom.total_janky_frames_with_long_cpu(), + atom.total_janky_frames_with_long_gpu(), + atom.total_janky_frames_sf_unattributed(), + atom.total_janky_frames_app_unattributed(), + atom.total_janky_frames_sf_scheduling(), + atom.total_jank_frames_sf_prediction_error(), + atom.total_jank_frames_app_buffer_stuffing(), + atom.display_refresh_rate_bucket(), deadlineMisses.value(), + predictionErrors.value(), atom.render_rate_bucket()); + } + return AStatsManager_PULL_SUCCESS; +} + +AStatsManager_PullAtomCallbackReturn SurfaceFlingerPuller::parseLayerInfoPull( + const std::string& protoData, AStatsEventList* data) { + android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList; + if (!atomList.ParseFromString(protoData)) { + ALOGW("Error parsing surface flinger layer stats to proto"); + return AStatsManager_PULL_SKIP; + } + + for (const auto& atom : atomList.atom()) { + // The strings must outlive the BytesFields, which only have a pointer to the data. + std::string present2PresentStr, post2presentStr, acquire2PresentStr, latch2PresentStr, + desired2PresentStr, post2AcquireStr, frameRateVoteStr, appDeadlineMissesStr; + optional present2Present = + getBytes(atom.present_to_present(), present2PresentStr); + optional post2present = getBytes(atom.post_to_present(), post2presentStr); + optional acquire2Present = + getBytes(atom.acquire_to_present(), acquire2PresentStr); + optional latch2Present = getBytes(atom.latch_to_present(), latch2PresentStr); + optional desired2Present = + getBytes(atom.desired_to_present(), desired2PresentStr); + optional post2Acquire = getBytes(atom.post_to_acquire(), post2AcquireStr); + optional frameRateVote = getBytes(atom.set_frame_rate_vote(), frameRateVoteStr); + optional appDeadlineMisses = + getBytes(atom.app_deadline_misses(), appDeadlineMissesStr); + + // Fail if any serialization to bytes failed. + if (!present2Present || !post2present || !acquire2Present || !latch2Present || + !desired2Present || !post2Acquire || !frameRateVote || !appDeadlineMisses) { + return AStatsManager_PULL_SKIP; + } + + android::util::addAStatsEvent(data, android::util::SURFACEFLINGER_STATS_LAYER_INFO, + atom.layer_name().c_str(), atom.total_frames(), + atom.dropped_frames(), present2Present.value(), + post2present.value(), acquire2Present.value(), + latch2Present.value(), desired2Present.value(), + post2Acquire.value(), atom.late_acquire_frames(), + atom.bad_desired_present_frames(), atom.uid(), + atom.total_timeline_frames(), atom.total_janky_frames(), + atom.total_janky_frames_with_long_cpu(), + atom.total_janky_frames_with_long_gpu(), + atom.total_janky_frames_sf_unattributed(), + atom.total_janky_frames_app_unattributed(), + atom.total_janky_frames_sf_scheduling(), + atom.total_jank_frames_sf_prediction_error(), + atom.total_jank_frames_app_buffer_stuffing(), + atom.display_refresh_rate_bucket(), atom.render_rate_bucket(), + frameRateVote.value(), appDeadlineMisses.value()); + } + return AStatsManager_PULL_SUCCESS; +} + +} // namespace stats +} // namespace server +} // namespace android diff --git a/services/core/jni/stats/SurfaceFlingerPuller.h b/services/core/jni/stats/SurfaceFlingerPuller.h new file mode 100644 index 0000000000000..301af1d64a222 --- /dev/null +++ b/services/core/jni/stats/SurfaceFlingerPuller.h @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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 +#include + +namespace android { +namespace server { +namespace stats { + +/** + * Pulls data from surfaceflinger. + * The indirection is needed because surfaceflinger is a bootstrap process. + */ +class SurfaceFlingerPuller { +public: + AStatsManager_PullAtomCallbackReturn pull(int32_t atomTag, AStatsEventList* data); + +private: + AStatsManager_PullAtomCallbackReturn parseGlobalInfoPull(const std::string& protoData, + AStatsEventList* data); + AStatsManager_PullAtomCallbackReturn parseLayerInfoPull(const std::string& protoData, + AStatsEventList* data); +}; + +} // namespace stats +} // namespace server +} // namespace android \ No newline at end of file