/* * Copyright (C) 2023 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 "gpu_counters" #include #include #include #include #include #include #include #define _LOG(level, msg, ...) \ do { \ fprintf(stderr, #level ": " msg "\n", ##__VA_ARGS__); \ ALOG##level(msg, ##__VA_ARGS__); \ } while (false) #define LOG_ERR(msg, ...) _LOG(E, msg, ##__VA_ARGS__) #define LOG_WARN(msg, ...) _LOG(W, msg, ##__VA_ARGS__) #define LOG_INFO(msg, ...) _LOG(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/gpu_counter_producer.pid"; static 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; } static 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); } static 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; } static void clearPidFile() { unlink(kPidFileName); } static void usage(const char* pname) { fprintf(stderr, "Starts the GPU hardware counter profiling Perfetto data producer.\n\n" "usage: %s [-hf]\n" " -f: run in the foreground.\n" " -h: this message.\n", pname); } // Program to load the GPU Perfetto producer .so and call start(). int main(int argc, char** argv) { const char* pname = argv[0]; bool foreground = false; int c; while ((c = getopt(argc, argv, "fh")) != -1) { switch (c) { case 'f': foreground = true; break; case '?': case ':': case 'h': usage(pname); return 1; } } if (optind < argc) { usage(pname); return 1; } if (!foreground) { daemon(0, 0); } if (getenv("LD_LIBRARY_PATH") == nullptr) { setenv("LD_LIBRARY_PATH", "/vendor/lib64:/vendor/lib", 0 /*override*/); LOG_INFO("execv with: LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH")); execvpe(pname, argv, environ); } if (!writeToPidFile()) { LOG_ERR("Could not open %s", kPidFileName); return 1; } dlerror(); // Clear any possibly ignored previous error. 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")); clearPidFile(); return 1; } LOG_INFO("Calling start at %p", startFunc); (*startFunc)(); LOG_WARN("Producer has exited."); clearPidFile(); return 0; }