1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "gpu_counters"
18 
19 #include <dlfcn.h>
20 #include <fcntl.h>
21 #include <log/log.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #define _LOG(level, msg, ...)                                 \
28     do {                                                      \
29         fprintf(stderr, #level ": " msg "\n", ##__VA_ARGS__); \
30         ALOG##level(msg, ##__VA_ARGS__);                      \
31     } while (false)
32 
33 #define LOG_ERR(msg, ...) _LOG(E, msg, ##__VA_ARGS__)
34 #define LOG_WARN(msg, ...) _LOG(W, msg, ##__VA_ARGS__)
35 #define LOG_INFO(msg, ...) _LOG(I, msg, ##__VA_ARGS__)
36 
37 #define NELEM(x) (sizeof(x) / sizeof(x[0]))
38 
39 typedef void (*FN_PTR)(void);
40 
41 const char* kProducerPaths[] = {
42         "libgpudataproducer.so",
43 };
44 const char* kPidFileName = "/data/local/tmp/gpu_counter_producer.pid";
45 
loadLibrary(const char * lib)46 static FN_PTR loadLibrary(const char* lib) {
47     char* error;
48 
49     LOG_INFO("Trying %s", lib);
50     void* handle = dlopen(lib, RTLD_GLOBAL);
51     if ((error = dlerror()) != nullptr || handle == nullptr) {
52         LOG_WARN("Error loading lib: %s", error);
53         return nullptr;
54     }
55 
56     FN_PTR startFunc = (FN_PTR)dlsym(handle, "start");
57     if ((error = dlerror()) != nullptr) {
58         LOG_ERR("Error looking for start symbol: %s", error);
59         dlclose(handle);
60         return nullptr;
61     }
62     return startFunc;
63 }
64 
killExistingProcess()65 static void killExistingProcess() {
66     int fd = open(kPidFileName, O_RDONLY);
67     if (fd == -1) {
68         return;
69     }
70     char pidString[10];
71     if (read(fd, pidString, 10) > 0) {
72         int pid = -1;
73         sscanf(pidString, "%d", &pid);
74         if (pid > 0) {
75             kill(pid, SIGINT);
76         }
77     }
78     close(fd);
79 }
80 
writeToPidFile()81 static bool writeToPidFile() {
82     killExistingProcess();
83     int fd = open(kPidFileName, O_CREAT | O_WRONLY | O_TRUNC,
84                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
85     if (fd == -1) {
86         return false;
87     }
88     pid_t pid = getpid();
89     char pidString[10];
90     sprintf(pidString, "%d", pid);
91     write(fd, pidString, strlen(pidString));
92     close(fd);
93     return true;
94 }
95 
clearPidFile()96 static void clearPidFile() {
97     unlink(kPidFileName);
98 }
99 
usage(const char * pname)100 static void usage(const char* pname) {
101     fprintf(stderr,
102             "Starts the GPU hardware counter profiling Perfetto data producer.\n\n"
103             "usage: %s [-hf]\n"
104             "   -f: run in the foreground.\n"
105             "   -h: this message.\n",
106             pname);
107 }
108 
109 // Program to load the GPU Perfetto producer .so and call start().
main(int argc,char ** argv)110 int main(int argc, char** argv) {
111     const char* pname = argv[0];
112     bool foreground = false;
113     int c;
114     while ((c = getopt(argc, argv, "fh")) != -1) {
115         switch (c) {
116             case 'f':
117                 foreground = true;
118                 break;
119             case '?':
120             case ':':
121             case 'h':
122                 usage(pname);
123                 return 1;
124         }
125     }
126 
127     if (optind < argc) {
128         usage(pname);
129         return 1;
130     }
131 
132     if (!foreground) {
133         daemon(0, 0);
134     }
135 
136     if (getenv("LD_LIBRARY_PATH") == nullptr) {
137         setenv("LD_LIBRARY_PATH", "/vendor/lib64:/vendor/lib", 0 /*override*/);
138         LOG_INFO("execv with: LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
139         execvpe(pname, argv, environ);
140     }
141 
142     if (!writeToPidFile()) {
143         LOG_ERR("Could not open %s", kPidFileName);
144         return 1;
145     }
146 
147     dlerror(); // Clear any possibly ignored previous error.
148     FN_PTR startFunc = nullptr;
149     for (int i = 0; startFunc == nullptr && i < NELEM(kProducerPaths); i++) {
150         startFunc = loadLibrary(kProducerPaths[i]);
151     }
152 
153     if (startFunc == nullptr) {
154         LOG_ERR("Did not find the producer library");
155         LOG_ERR("LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
156         clearPidFile();
157         return 1;
158     }
159 
160     LOG_INFO("Calling start at %p", startFunc);
161     (*startFunc)();
162     LOG_WARN("Producer has exited.");
163 
164     clearPidFile();
165     return 0;
166 }
167