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
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <jni.h>
|
||||
#include <log/log.h>
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <stats_event.h>
|
||||
#include <stats_pull_atom_callback.h>
|
||||
#include <statslog.h>
|
||||
|
||||
#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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
8
services/core/jni/stats/OWNERS
Normal file
8
services/core/jni/stats/OWNERS
Normal file
@@ -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
|
||||
168
services/core/jni/stats/SurfaceFlingerPuller.cpp
Normal file
168
services/core/jni/stats/SurfaceFlingerPuller.cpp
Normal file
@@ -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 <gui/SurfaceComposerClient.h>
|
||||
#include <log/log.h>
|
||||
#include <statslog.h>
|
||||
#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace server {
|
||||
namespace stats {
|
||||
|
||||
using android::util::BytesField;
|
||||
using std::optional;
|
||||
|
||||
namespace {
|
||||
optional<BytesField> 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<BytesField> frameDuration = getBytes(atom.frame_duration(), frameDurationStr);
|
||||
optional<BytesField> renderEngineTime =
|
||||
getBytes(atom.render_engine_timing(), renderEngineTimeStr);
|
||||
optional<BytesField> deadlineMisses =
|
||||
getBytes(atom.sf_deadline_misses(), deadlineMissesStr);
|
||||
optional<BytesField> 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<BytesField> present2Present =
|
||||
getBytes(atom.present_to_present(), present2PresentStr);
|
||||
optional<BytesField> post2present = getBytes(atom.post_to_present(), post2presentStr);
|
||||
optional<BytesField> acquire2Present =
|
||||
getBytes(atom.acquire_to_present(), acquire2PresentStr);
|
||||
optional<BytesField> latch2Present = getBytes(atom.latch_to_present(), latch2PresentStr);
|
||||
optional<BytesField> desired2Present =
|
||||
getBytes(atom.desired_to_present(), desired2PresentStr);
|
||||
optional<BytesField> post2Acquire = getBytes(atom.post_to_acquire(), post2AcquireStr);
|
||||
optional<BytesField> frameRateVote = getBytes(atom.set_frame_rate_vote(), frameRateVoteStr);
|
||||
optional<BytesField> 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
|
||||
44
services/core/jni/stats/SurfaceFlingerPuller.h
Normal file
44
services/core/jni/stats/SurfaceFlingerPuller.h
Normal file
@@ -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 <stats_event.h>
|
||||
#include <stats_pull_atom_callback.h>
|
||||
#include <utils/String16.h>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user