diff options
Diffstat (limited to 'libstats/socket_lazy/libstatssocket_lazy.cpp')
-rw-r--r-- | libstats/socket_lazy/libstatssocket_lazy.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/libstats/socket_lazy/libstatssocket_lazy.cpp b/libstats/socket_lazy/libstatssocket_lazy.cpp new file mode 100644 index 000000000..dd93eebf0 --- /dev/null +++ b/libstats/socket_lazy/libstatssocket_lazy.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 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. + */ + +#include "libstatssocket_lazy.h" + +#include <mutex> + +#include <dlfcn.h> +#include <stdatomic.h> + +#include "log/log.h" + +#include "stats_event.h" +#include "stats_socket.h" + +// This file provides a lazy interface to libstatssocket.so to address early boot dependencies. +// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and +// libstatssocket.so is in the statsd APEX. + +// Method pointers to libstatssocket methods are held in an array which simplifies checking +// all pointers are initialized. +enum MethodIndex { + // Stats Event APIs in stats_event.h. + k_AStatsEvent_obtain, + k_AStatsEvent_build, + k_AStatsEvent_write, + k_AStatsEvent_release, + k_AStatsEvent_setAtomId, + k_AStatsEvent_writeInt32, + k_AStatsEvent_writeInt64, + k_AStatsEvent_writeFloat, + k_AStatsEvent_writeBool, + k_AStatsEvent_writeByteArray, + k_AStatsEvent_writeString, + k_AStatsEvent_writeAttributionChain, + k_AStatsEvent_addBoolAnnotation, + k_AStatsEvent_addInt32Annotation, + + // Stats Socket APIs in stats_socket.h. + k_AStatsSocket_close, + + // Marker for count of methods + k_MethodCount +}; + +// Table of methods pointers in libstatssocket APIs. +static void* g_Methods[k_MethodCount]; + +// +// Libstatssocket lazy loading. +// + +static atomic_bool gPreventLibstatssocketLoading = false; // Allows tests to block loading. + +void PreventLibstatssocketLazyLoadingForTests() { + gPreventLibstatssocketLoading.store(true); +} + +static void* LoadLibstatssocket(int dlopen_flags) { + if (gPreventLibstatssocketLoading.load()) { + return nullptr; + } + return dlopen("libstatssocket.so", dlopen_flags); +} + +// +// Initialization and symbol binding. + +static void BindSymbol(void* handle, const char* name, enum MethodIndex index) { + void* symbol = dlsym(handle, name); + LOG_ALWAYS_FATAL_IF(symbol == nullptr, "Failed to find symbol '%s' in libstatssocket.so: %s", + name, dlerror()); + g_Methods[index] = symbol; +} + +static void InitializeOnce() { + void* handle = LoadLibstatssocket(RTLD_NOW); + LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load libstatssocket.so: %s", dlerror()); + +#undef BIND_SYMBOL +#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name); + // Methods in stats_event.h. + BIND_SYMBOL(AStatsEvent_obtain); + BIND_SYMBOL(AStatsEvent_build); + BIND_SYMBOL(AStatsEvent_write); + BIND_SYMBOL(AStatsEvent_release); + BIND_SYMBOL(AStatsEvent_setAtomId); + BIND_SYMBOL(AStatsEvent_writeInt32); + BIND_SYMBOL(AStatsEvent_writeInt64); + BIND_SYMBOL(AStatsEvent_writeFloat); + BIND_SYMBOL(AStatsEvent_writeBool); + BIND_SYMBOL(AStatsEvent_writeByteArray); + BIND_SYMBOL(AStatsEvent_writeString); + BIND_SYMBOL(AStatsEvent_writeAttributionChain); + BIND_SYMBOL(AStatsEvent_addBoolAnnotation); + BIND_SYMBOL(AStatsEvent_addInt32Annotation); + + // Methods in stats_socket.h. + BIND_SYMBOL(AStatsSocket_close); +#undef BIND_SYMBOL + + // Check every symbol is bound. + for (int i = 0; i < k_MethodCount; ++i) { + LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr, + "Uninitialized method in libstatssocket_lazy at index: %d", i); + } +} + +static void EnsureInitialized() { + static std::once_flag initialize_flag; + std::call_once(initialize_flag, InitializeOnce); +} + +#define INVOKE_METHOD(name, args...) \ + do { \ + EnsureInitialized(); \ + void* method = g_Methods[k_##name]; \ + return reinterpret_cast<decltype(&name)>(method)(args); \ + } while (0) + +// +// Forwarding for methods in stats_event.h. +// + +AStatsEvent* AStatsEvent_obtain() { + INVOKE_METHOD(AStatsEvent_obtain); +} + +void AStatsEvent_build(AStatsEvent* event) { + INVOKE_METHOD(AStatsEvent_build, event); +} + +int AStatsEvent_write(AStatsEvent* event) { + INVOKE_METHOD(AStatsEvent_write, event); +} + +void AStatsEvent_release(AStatsEvent* event) { + INVOKE_METHOD(AStatsEvent_release, event); +} + +void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) { + INVOKE_METHOD(AStatsEvent_setAtomId, event, atomId); +} + +void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) { + INVOKE_METHOD(AStatsEvent_writeInt32, event, value); +} + +void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) { + INVOKE_METHOD(AStatsEvent_writeInt64, event, value); +} + +void AStatsEvent_writeFloat(AStatsEvent* event, float value) { + INVOKE_METHOD(AStatsEvent_writeFloat, event, value); +} + +void AStatsEvent_writeBool(AStatsEvent* event, bool value) { + INVOKE_METHOD(AStatsEvent_writeBool, event, value); +} + +void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) { + INVOKE_METHOD(AStatsEvent_writeByteArray, event, buf, numBytes); +} + +void AStatsEvent_writeString(AStatsEvent* event, const char* value) { + INVOKE_METHOD(AStatsEvent_writeString, event, value); +} + +void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids, + const char* const* tags, uint8_t numNodes) { + INVOKE_METHOD(AStatsEvent_writeAttributionChain, event, uids, tags, numNodes); +} + +void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) { + INVOKE_METHOD(AStatsEvent_addBoolAnnotation, event, annotationId, value); +} + +void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) { + INVOKE_METHOD(AStatsEvent_addInt32Annotation, event, annotationId, value); +} + +// +// Forwarding for methods in stats_socket.h. +// + +void AStatsSocket_close() { + INVOKE_METHOD(AStatsSocket_close); +}
\ No newline at end of file |