xref: /aosp_15_r20/external/trace-cmd/tracecmd/trace-agent.c (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2018 VMware Inc, Slavomir Kaslev <[email protected]>
4  *
5  * based on prior implementation by Yoshihiro Yunomae
6  * Copyright (C) 2013 Hitachi, Ltd.
7  * Yoshihiro YUNOMAE <[email protected]>
8  */
9 
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <signal.h>
14 #include <stdbool.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/socket.h>
18 #include <sys/wait.h>
19 #include <unistd.h>
20 #include <pthread.h>
21 
22 #include "trace-local.h"
23 #include "trace-msg.h"
24 
25 #define dprint(fmt, ...)	tracecmd_debug(fmt, ##__VA_ARGS__)
26 
make_vsocks(int nr,int * fds,unsigned int * ports)27 static void make_vsocks(int nr, int *fds, unsigned int *ports)
28 {
29 	unsigned int port;
30 	int i, fd, ret;
31 
32 	for (i = 0; i < nr; i++) {
33 		fd = trace_vsock_make_any();
34 		if (fd < 0)
35 			die("Failed to open vsocket");
36 
37 		ret = trace_vsock_get_port(fd, &port);
38 		if (ret < 0)
39 			die("Failed to get vsocket address");
40 
41 		fds[i] = fd;
42 		ports[i] = port;
43 	}
44 }
45 
make_net(int nr,int * fds,unsigned int * ports)46 static void make_net(int nr, int *fds, unsigned int *ports)
47 {
48 	int port;
49 	int i, fd;
50 	int start_port = START_PORT_SEARCH;
51 
52 	for (i = 0; i < nr; i++) {
53 		port = trace_net_search(start_port, &fd, USE_TCP);
54 		if (port < 0)
55 			die("Failed to open socket");
56 		if (listen(fd, 5) < 0)
57 			die("Failed to listen on port %d\n", port);
58 		fds[i] = fd;
59 		ports[i] = port;
60 		dprint("CPU[%d]: fd:%d port:%d\n", i, fd, port);
61 		start_port = port + 1;
62 	}
63 }
64 
make_sockets(int nr,int * fds,unsigned int * ports,const char * network)65 static void make_sockets(int nr, int *fds, unsigned int *ports,
66 			 const char * network)
67 {
68 	if (network)
69 		return make_net(nr, fds, ports);
70 	else
71 		return make_vsocks(nr, fds, ports);
72 }
73 
open_agent_fifos(int nr_cpus,int * fds)74 static int open_agent_fifos(int nr_cpus, int *fds)
75 {
76 	char path[PATH_MAX];
77 	int i, fd, ret;
78 
79 	for (i = 0; i < nr_cpus; i++) {
80 		snprintf(path, sizeof(path), VIRTIO_FIFO_FMT, i);
81 		fd = open(path, O_WRONLY);
82 		if (fd < 0) {
83 			ret = -errno;
84 			goto cleanup;
85 		}
86 
87 		fds[i] = fd;
88 	}
89 
90 	return 0;
91 
92 cleanup:
93 	while (--i >= 0)
94 		close(fds[i]);
95 
96 	return ret;
97 }
98 
get_clock(int argc,char ** argv)99 static char *get_clock(int argc, char **argv)
100 {
101 	int i;
102 
103 	if (!argc || !argv)
104 		return NULL;
105 
106 	for (i = 0; i < argc - 1; i++) {
107 		if (!strcmp("-C", argv[i]))
108 			return argv[i+1];
109 	}
110 	return NULL;
111 }
112 
trace_print_connection(int fd,const char * network)113 static void trace_print_connection(int fd, const char *network)
114 {
115 	int ret;
116 
117 	if (network)
118 		ret = trace_net_print_connection(fd);
119 	else
120 		ret = trace_vsock_print_connection(fd);
121 	if (ret < 0)
122 		tracecmd_debug("Could not print connection fd:%d\n", fd);
123 }
124 
agent_handle(int sd,int nr_cpus,int page_size,const char * network)125 static void agent_handle(int sd, int nr_cpus, int page_size, const char *network)
126 {
127 	struct tracecmd_tsync_protos *tsync_protos = NULL;
128 	struct tracecmd_time_sync *tsync = NULL;
129 	struct tracecmd_msg_handle *msg_handle;
130 	char *tsync_proto = NULL;
131 	unsigned long long trace_id;
132 	unsigned int remote_id;
133 	unsigned int local_id;
134 	unsigned int tsync_port = 0;
135 	unsigned int *ports;
136 	char **argv = NULL;
137 	int argc = 0;
138 	bool use_fifos;
139 	int *fds;
140 	int ret;
141 	int fd;
142 
143 	fds = calloc(nr_cpus, sizeof(*fds));
144 	ports = calloc(nr_cpus, sizeof(*ports));
145 	if (!fds || !ports)
146 		die("Failed to allocate memory");
147 
148 	msg_handle = tracecmd_msg_handle_alloc(sd, 0);
149 	if (!msg_handle)
150 		die("Failed to allocate message handle");
151 
152 	ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
153 					  &use_fifos, &trace_id,
154 					  &tsync_protos);
155 	if (ret < 0)
156 		die("Failed to receive trace request");
157 
158 	if (use_fifos && open_agent_fifos(nr_cpus, fds))
159 		use_fifos = false;
160 
161 	if (!use_fifos)
162 		make_sockets(nr_cpus, fds, ports, network);
163 	if (tsync_protos && tsync_protos->names) {
164 		if (network) {
165 			/* For now just use something */
166 			remote_id = 2;
167 			local_id = 1;
168 			tsync_port = trace_net_search(START_PORT_SEARCH, &fd, USE_TCP);
169 			if (listen(fd, 5) < 0)
170 				die("Failed to listen on %d\n", tsync_port);
171 		} else {
172 			if (get_vsocket_params(msg_handle->fd, &local_id,
173 					       &remote_id)) {
174 				warning("Failed to get local and remote ids");
175 				/* Just make something up */
176 				remote_id = -1;
177 				local_id = -2;
178 			}
179 			fd = trace_vsock_make_any();
180 			if (fd >= 0 &&
181 			    trace_vsock_get_port(fd, &tsync_port) < 0) {
182 				close(fd);
183 				fd = -1;
184 			}
185 		}
186 		if (fd >= 0) {
187 			tsync = tracecmd_tsync_with_host(fd, tsync_protos,
188 							 get_clock(argc, argv),
189 							 remote_id, local_id);
190 		}
191 		if (tsync) {
192 			tracecmd_tsync_get_selected_proto(tsync, &tsync_proto);
193 		} else {
194 			warning("Failed to negotiate timestamps synchronization with the host");
195 			if (fd >= 0)
196 				close(fd);
197 		}
198 	}
199 	trace_id = tracecmd_generate_traceid();
200 	ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
201 					   ports, use_fifos, trace_id,
202 					   tsync_proto, tsync_port);
203 	if (ret < 0)
204 		die("Failed to send trace response");
205 
206 	trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
207 			   use_fifos, trace_id, network);
208 
209 	if (tsync) {
210 		tracecmd_tsync_with_host_stop(tsync);
211 		tracecmd_tsync_free(tsync);
212 	}
213 
214 	if (tsync_protos) {
215 		free(tsync_protos->names);
216 		free(tsync_protos);
217 	}
218 	free(argv[0]);
219 	free(argv);
220 	free(ports);
221 	free(fds);
222 	tracecmd_msg_handle_close(msg_handle);
223 	exit(0);
224 }
225 
226 static volatile pid_t handler_pid;
227 
handle_sigchld(int sig)228 static void handle_sigchld(int sig)
229 {
230 	int wstatus;
231 	pid_t pid;
232 
233 	for (;;) {
234 		pid = waitpid(-1, &wstatus, WNOHANG);
235 		if (pid <= 0)
236 			break;
237 
238 		if (pid == handler_pid)
239 			handler_pid = 0;
240 	}
241 }
242 
do_fork()243 static pid_t do_fork()
244 {
245 	/* in debug mode, we do not fork off children */
246 	if (tracecmd_get_debug())
247 		return 0;
248 
249 	return fork();
250 }
251 
agent_serve(unsigned int port,bool do_daemon,const char * network)252 static void agent_serve(unsigned int port, bool do_daemon, const char *network)
253 {
254 	struct sockaddr_storage net_addr;
255 	struct sockaddr *addr = NULL;
256 	socklen_t *addr_len_p = NULL;
257 	socklen_t addr_len = sizeof(net_addr);
258 	int sd, cd, nr_cpus;
259 	unsigned int cid;
260 	pid_t pid;
261 
262 	signal(SIGCHLD, handle_sigchld);
263 
264 	if (network) {
265 		addr = (struct sockaddr *)&net_addr;
266 		addr_len_p = &addr_len;
267 	}
268 
269 	nr_cpus = tracecmd_count_cpus();
270 	page_size = getpagesize();
271 
272 	if (network) {
273 		sd = trace_net_make(port, USE_TCP);
274 		if (listen(sd, 5) < 0)
275 			die("Failed to listen on %d\n", port);
276 	} else
277 		sd = trace_vsock_make(port);
278 	if (sd < 0)
279 		die("Failed to open socket");
280 	tracecmd_tsync_init();
281 
282 	if (!network) {
283 		cid = trace_vsock_local_cid();
284 		if (cid >= 0)
285 			printf("listening on @%u:%u\n", cid, port);
286 	}
287 
288 	if (do_daemon && daemon(1, 0))
289 		die("daemon");
290 
291 	for (;;) {
292 		cd = accept(sd, addr, addr_len_p);
293 		if (cd < 0) {
294 			if (errno == EINTR)
295 				continue;
296 			die("accept");
297 		}
298 		if (tracecmd_get_debug())
299 			trace_print_connection(cd, network);
300 
301 		if (network && !trace_net_cmp_connection(&net_addr, network)) {
302 			dprint("Client does not match '%s'\n", network);
303 			close(cd);
304 			continue;
305 		}
306 
307 		if (handler_pid)
308 			goto busy;
309 
310 		pid = do_fork();
311 		if (pid == 0) {
312 			close(sd);
313 			signal(SIGCHLD, SIG_DFL);
314 			agent_handle(cd, nr_cpus, page_size, network);
315 		}
316 		if (pid > 0)
317 			handler_pid = pid;
318 
319 busy:
320 		close(cd);
321 	}
322 }
323 
324 enum {
325 	OPT_verbose	= 254,
326 	DO_DEBUG	= 255
327 };
328 
trace_agent(int argc,char ** argv)329 void trace_agent(int argc, char **argv)
330 {
331 	bool do_daemon = false;
332 	unsigned int port = TRACE_AGENT_DEFAULT_PORT;
333 	const char *network = NULL;
334 
335 	if (argc < 2)
336 		usage(argv);
337 
338 	if (strcmp(argv[1], "agent") != 0)
339 		usage(argv);
340 
341 	for (;;) {
342 		int c, option_index = 0;
343 		static struct option long_options[] = {
344 			{"port", required_argument, NULL, 'p'},
345 			{"help", no_argument, NULL, '?'},
346 			{"debug", no_argument, NULL, DO_DEBUG},
347 			{"verbose", optional_argument, NULL, OPT_verbose},
348 			{NULL, 0, NULL, 0}
349 		};
350 
351 		c = getopt_long(argc-1, argv+1, "+hp:DN:",
352 				long_options, &option_index);
353 		if (c == -1)
354 			break;
355 		switch (c) {
356 		case 'h':
357 			usage(argv);
358 			break;
359 		case 'N':
360 			network = optarg;
361 			break;
362 		case 'p':
363 			port = atoi(optarg);
364 			break;
365 		case 'D':
366 			do_daemon = true;
367 			break;
368 		case DO_DEBUG:
369 			tracecmd_set_debug(true);
370 			break;
371 		case OPT_verbose:
372 			if (trace_set_verbose(optarg) < 0)
373 				die("invalid verbose level %s", optarg);
374 			break;
375 		default:
376 			usage(argv);
377 		}
378 	}
379 
380 	if (optind < argc-1)
381 		usage(argv);
382 
383 	agent_serve(port, do_daemon, network);
384 }
385