From 20db5f5475a6930b3427baebcb0eb56e5189d657 Mon Sep 17 00:00:00 2001 From: Emre Kultursay Date: Mon, 13 Apr 2026 11:25:59 -0700 Subject: [PATCH] Add agi_launch_producer binary Built similar to the Vulkan layers .so files. --- .github/workflows/ci.yml | 2 +- BUILD.md | 2 +- CMakeLists.txt | 4 + agi_launch_producer/CMakeLists.txt | 12 +++ agi_launch_producer/main.cpp | 125 +++++++++++++++++++++++++++++ scripts/android.py | 6 +- 6 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 agi_launch_producer/CMakeLists.txt create mode 100644 agi_launch_producer/main.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efe3df5e4c..e18a5d04d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: layers-${{ github.run_id }} - path: build-android/libs/lib/**/* + path: build-android/install/**/* if-no-files-found: 'error' macos: diff --git a/BUILD.md b/BUILD.md index 94a6c6baf7..548542e27b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -236,7 +236,7 @@ https://en.wikipedia.org/wiki/Apk_(file_format)#Package_contents Alternatively users can also use `scripts/android.py` to build the binaries. -Note: `scripts/android.py` will place the binaries in the `build-android/libs` directory. +Note: `scripts/android.py` will place the binaries in the `build-android/install` directory. ```sh # Build release binary for arm64-v8a diff --git a/CMakeLists.txt b/CMakeLists.txt index df25815da8..b203849458 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,10 @@ if(BUILD_APIDUMP OR BUILD_MONITOR OR BUILD_SCREENSHOT OR BUILD_CPUTIMING) add_subdirectory(layersvt) endif() +if(ANDROID) + add_subdirectory(agi_launch_producer) +endif() + if(BUILD_LAYERMGR) if(NOT Qt6_FOUND) message("WARNING: Vulkan Configurator will be excluded from the build because Qt6 was not found. Please add Qt6 into the PATH environment variable") diff --git a/agi_launch_producer/CMakeLists.txt b/agi_launch_producer/CMakeLists.txt new file mode 100644 index 0000000000..c725ceaa37 --- /dev/null +++ b/agi_launch_producer/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.22.1) +project(agi_launch_producer LANGUAGES CXX) + +if(NOT ANDROID) + return() +endif() + +add_executable(agi_launch_producer main.cpp) + +target_link_libraries(agi_launch_producer PRIVATE log) + +install(TARGETS agi_launch_producer DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/agi_launch_producer/main.cpp b/agi_launch_producer/main.cpp new file mode 100644 index 0000000000..74b7496677 --- /dev/null +++ b/agi_launch_producer/main.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2026 Google Inc. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#define _LOG(lvl, name, msg, ...) \ + do { \ + fprintf(stderr, name ": " msg "\n", ##__VA_ARGS__); \ + __android_log_print(lvl, "AGI", msg, ##__VA_ARGS__); \ + } while (false) + +#define LOG_ERR(msg, ...) _LOG(ANDROID_LOG_ERROR, "E", msg, ##__VA_ARGS__) +#define LOG_WARN(msg, ...) _LOG(ANDROID_LOG_WARN, "W", msg, ##__VA_ARGS__) +#define LOG_INFO(msg, ...) _LOG(ANDROID_LOG_INFO, "I", msg, ##__VA_ARGS__) + +#define NELEM(x) (sizeof(x) / sizeof(x[0])) + +typedef void (*FN_PTR)(void); + +const char* kProducerPaths[] = { + "libgpudataproducer.so", +}; +const char* kPidFileName = "/data/local/tmp/agi_launch_producer.pid"; + +FN_PTR loadLibrary(const char* lib) { + char* error; + + LOG_INFO("Trying %s", lib); + void* handle = dlopen(lib, RTLD_GLOBAL); + if ((error = dlerror()) != nullptr || handle == nullptr) { + LOG_WARN("Error loading lib: %s", error); + return nullptr; + } + + FN_PTR startFunc = (FN_PTR)dlsym(handle, "start"); + if ((error = dlerror()) != nullptr) { + LOG_ERR("Error looking for start symbol: %s", error); + dlclose(handle); + return nullptr; + } + return startFunc; +} + +// If a previous producer has died without cleaning up its pidfile, +// here we kill a PID that may be related to another process. +// This is a risk we take, it would be rare for a previous PID to be reused, +// and in the worst case we kill a non-critical application as core services +// are not killable that easily. +void killExistingProcess() { + int fd = open(kPidFileName, O_RDONLY); + if (fd == -1) { + return; + } + char pidString[10]; + if (read(fd, pidString, 10) > 0) { + int pid = -1; + sscanf(pidString, "%d", &pid); + if (pid > 0) { + kill(pid, SIGINT); + } + } + close(fd); +} + +bool writeToPidFile() { + killExistingProcess(); + int fd = open(kPidFileName, O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd == -1) { + return false; + } + pid_t pid = getpid(); + char pidString[10]; + sprintf(pidString, "%d", pid); + write(fd, pidString, strlen(pidString)); + close(fd); + return true; +} + +// Program to load the GPU Perfetto producer .so and call start(). +int main(int argc, char** argv) { + if (!writeToPidFile()) { + LOG_ERR("Could not open %s", kPidFileName); + std::abort(); + } + + dlerror(); + FN_PTR startFunc = nullptr; + for (int i = 0; startFunc == nullptr && i < NELEM(kProducerPaths); i++) { + startFunc = loadLibrary(kProducerPaths[i]); + } + + if (startFunc == nullptr) { + LOG_ERR("Did not find the producer library"); + LOG_ERR("LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH")); + std::abort(); + } + + LOG_INFO("Calling start at %p", startFunc); + (*startFunc)(); + LOG_WARN("Producer has exited."); + + return 0; +} diff --git a/scripts/android.py b/scripts/android.py index 2ef39958bb..db9bd09d63 100755 --- a/scripts/android.py +++ b/scripts/android.py @@ -84,7 +84,7 @@ def main(): print(f"Using {tool} : {path}") - cmake_install_dir = RepoRelative('build-android/libs') + cmake_install_dir = RepoRelative('build-android/install') # Delete install directory since it could contain files from old runs if os.path.isdir(cmake_install_dir): @@ -93,7 +93,8 @@ def main(): for abi in android_abis: build_dir = RepoRelative(f'build-android/cmake/{abi}') - lib_dir = f'lib/{abi}' + lib_dir = f'{abi}/lib' + bin_dir = f'{abi}/bin' if clean: print("Deleting CMakeCache.txt") @@ -111,6 +112,7 @@ def main(): cmake_cmd += f' -D CMAKE_TOOLCHAIN_FILE={android_toolchain}' cmake_cmd += f' -D CMAKE_ANDROID_ARCH_ABI={abi}' cmake_cmd += f' -D CMAKE_INSTALL_LIBDIR={lib_dir}' + cmake_cmd += f' -D CMAKE_INSTALL_BINDIR={bin_dir}' cmake_cmd += f' -D BUILD_TESTS={build_tests}' cmake_cmd += f' -D CMAKE_ANDROID_STL_TYPE=c++_static'