1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <sys/mman.h>
12 #include <sys/statvfs.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <time.h>
16 #include <dirent.h>
17 #include <signal.h>
18 #include <errno.h>
19 #include <pthread.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <numa.h>
23 
24 #include "acrntrace.h"
25 
26 #define TIMER_ID	(128)
27 static uint32_t timeout = 0;
28 
29 static int exiting = 0;
30 
31 /* for opt */
32 static uint64_t period = 10000;
33 static const char optString[] = "i:hcrt:a:";
34 static const char dev_prefix[] = "acrn_trace_";
35 
36 static uint32_t flags = FLAG_CLEAR_BUF;
37 static char trace_file_dir[TRACE_FILE_DIR_LEN];
38 
39 static reader_struct *reader;
40 static int dev_cnt = 0; /* Count of /dev/acrn_trace_xxx devices */
41 
42 static struct bitmask *cpu_bitmask = NULL;
43 
display_usage(void)44 static void display_usage(void)
45 {
46 	printf("acrntrace - tool to collect ACRN trace data\n"
47 	       "[Usage] acrntrace [-i period] [-t max_time] [-ch]\n\n"
48 	       "[Options]\n"
49 	       "\t-h: print this message\n"
50 	       "\t-i: period_in_ms: specify polling interval [1-999]\n"
51 	       "\t-t: max time to capture trace data (in second)\n"
52 	       "\t-c: clear the buffered old data (deprecated)\n"
53 	       "\t-r: capture the buffered old data instead of clearing it\n"
54 	       "\t-a: cpu-set: only capture the trace data on these configured cpu-set\n");
55 }
56 
timer_handler(union sigval sv)57 static void timer_handler(union sigval sv)
58 {
59 	exiting = 1;
60 	exit(0);
61 }
62 
init_timer(int timeout)63 static int init_timer(int timeout)
64 {
65 	struct sigevent event;
66 	struct itimerspec it;
67 	timer_t tm_id;
68 	int err;
69 
70 	memset(&event, 0, sizeof(struct sigevent));
71 
72 	event.sigev_value.sival_int = TIMER_ID;
73 	event.sigev_notify = SIGEV_THREAD;
74 	event.sigev_notify_function = timer_handler;
75 
76 	err = timer_create(CLOCK_MONOTONIC, &event, &tm_id);
77 	if (err < 0) {
78 		pr_err("Error to create timer, errno(%d)\n", err);
79 		return err;
80 	}
81 
82 	it.it_interval.tv_sec = timeout;
83 	it.it_interval.tv_nsec = 0;
84 	it.it_value.tv_sec = timeout;
85 	it.it_value.tv_nsec = 0;
86 
87 	err = timer_settime(tm_id, 0, &it, NULL);
88 	if (err < 0) {
89 		pr_err("Error to set timer, errno(%d)\n", err);
90 		return err;
91 	}
92 
93 	pr_info("Capture trace data for about %ds and exit\n", timeout);
94 	return 0;
95 }
96 
parse_opt(int argc,char * argv[])97 static int parse_opt(int argc, char *argv[])
98 {
99 	int opt, ret;
100 
101 	while ((opt = getopt(argc, argv, optString)) != -1) {
102 		switch (opt) {
103 		case 'i':
104 			ret = strtol(optarg, NULL, 10);
105 			if (ret <= 0 || ret >=1000) {
106 				pr_err("'-i' require integer between [1-999]\n");
107 				return -EINVAL;
108 			}
109 			period = ret * 1000;
110 			pr_dbg("Period is %lu\n", period);
111 			break;
112 		case 't':
113 			ret = strtol(optarg, NULL, 10);
114 			if (ret <= 0) {
115 				pr_err("'-t' require integer greater than 0\n");
116 				return -EINVAL;
117 			}
118 			timeout = ret;
119 			pr_dbg("Capture trace data for at most %ds\n", ret);
120 			break;
121 		/*
122 		 * We have set the FLAG_CLEAR_BUF by default.
123 		 * Here we just keep the -c for backward compatibility.
124 		 */
125 		case 'c':
126 			break;
127 		case 'r':
128 			flags &= ~FLAG_CLEAR_BUF;
129 			break;
130 		case 'a':
131 			cpu_bitmask = numa_parse_cpustring_all(optarg);
132 			break;
133 		case 'h':
134 			display_usage();
135 			return -EINVAL;
136 		default:
137 			/* Undefined operation. */
138 			display_usage();
139 			return -EINVAL;
140 		}
141 	};
142 	return 0;
143 }
144 
get_dev_cnt(void)145 static int get_dev_cnt(void)
146 {
147 	struct dirent *pdir;
148 	int cnt = 0;
149 	char *ret;
150 	DIR *dir;
151 
152 	dir = opendir("/dev");
153 	if (!dir) {
154 		printf("Error opening /dev: %s\n", strerror(errno));
155 		return -1;
156 	}
157 
158 	while ((pdir = readdir(dir)) != NULL) {
159 		ret = strstr(pdir->d_name, dev_prefix);
160 		if (ret)
161 			cnt++;
162 	}
163 
164 	closedir(dir);
165 
166 	return cnt;
167 }
168 
create_trace_file_dir(char * dir)169 static int create_trace_file_dir(char *dir)
170 {
171 	int err = 0, ret;
172 	char time_str[TIME_STR_LEN + 1];
173 	time_t timep;
174 	struct tm *p;
175 	struct stat st;
176 
177 	time(&timep);
178 	p = localtime(&timep);
179 	if (p) {
180 		ret = snprintf(time_str, TIME_STR_LEN + 1,
181 			       "%04d%02d%02d-%02d%02d%02d",
182 			       (1900 + p->tm_year), (1 + p->tm_mon),
183 			       p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
184 		if (ret > TIME_STR_LEN)
185 			printf("WARN: time_str may be truncated\n");
186 	} else {
187 		if (snprintf(time_str, TIME_STR_LEN, "00000000-000000") >= TIME_STR_LEN)
188 			printf("WARN: time_str is truncated\n\n");
189 	}
190 
191 	pr_info("start tracing at %s\n", time_str);
192 
193 	if (stat(TRACE_FILE_ROOT, &st)) {
194 		err = mkdir(TRACE_FILE_ROOT, 0644);
195 		if (err) {
196 			pr_err("Fail to create dir %s, Error: %s\n",
197 				TRACE_FILE_ROOT, strerror(errno));
198 			return -1;
199 		}
200 	}
201 
202 	ret = snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s",
203 		       TRACE_FILE_ROOT, time_str);
204 	if (ret >= TRACE_FILE_DIR_LEN)
205 		printf("WARN: trace file dir name is truncated\n");
206 	if (stat(dir, &st)) {
207 		err = mkdir(dir, 0644);
208 		if (err) {
209 			pr_err("Fail to create dir %s, Error: %s\n",
210 				dir, strerror(errno));
211 			return -1;
212 		}
213 	}
214 
215 	pr_dbg("dir %s creted\n", dir);
216 
217 	return err;
218 }
219 
220 /* function executed in each consumer thread */
reader_fn(param_t * param)221 static void reader_fn(param_t * param)
222 {
223 	int ret;
224 	int fd = param->trace_fd;
225 	shared_buf_t *sbuf = param->sbuf;
226 
227 	pr_dbg("reader thread[%lu] created for FILE*[0x%p]\n",
228 	       pthread_self(), fp);
229 
230 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
231 	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
232 
233 	/* Clear the old data in sbuf */
234 	if (flags & FLAG_CLEAR_BUF)
235 		sbuf_clear_buffered(sbuf);
236 
237 	while (1) {
238 		do {
239 			ret = sbuf_write(fd, sbuf);
240 		} while (ret > 0);
241 
242 		usleep(period);
243 	}
244 }
245 
create_reader(reader_struct * reader,uint32_t dev_id)246 static int create_reader(reader_struct * reader, uint32_t dev_id)
247 {
248 	char trace_file_name[TRACE_FILE_NAME_LEN];
249 
250 	if (snprintf(reader->dev_name, DEV_PATH_LEN, "/dev/%s%u", dev_prefix, dev_id)
251 			>= DEV_PATH_LEN)
252 		printf("WARN: device name is truncated\n");
253 
254 	reader->param.devid = dev_id;
255 
256 	reader->dev_fd = open(reader->dev_name, O_RDWR);
257 	if (reader->dev_fd < 0) {
258 		pr_err("Failed to open %s, err %d\n", reader->dev_name, errno);
259 		reader->dev_fd = 0;
260 		return -1;
261 	}
262 
263 	reader->param.sbuf = mmap(NULL, MMAP_SIZE,
264 				  PROT_READ | PROT_WRITE,
265 				  MAP_SHARED, reader->dev_fd, 0);
266 	if (reader->param.sbuf == MAP_FAILED) {
267 		pr_err("mmap failed for %s, errno %d\n", reader->dev_name, errno);
268 		reader->param.sbuf = NULL;
269 		return -2;
270 	}
271 
272 	pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n",
273 	       dev_id, reader->param.sbuf->magic, reader->param.sbuf->ele_num,
274 	       reader->param.sbuf->ele_size);
275 
276 	if(snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir,
277 		 dev_id) >= TRACE_FILE_NAME_LEN)
278 		printf("WARN: trace file name is truncated\n");
279 	reader->param.trace_fd = open(trace_file_name,
280 					O_WRONLY | O_CREAT | O_TRUNC, 0644);
281 	if (!reader->param.trace_fd) {
282 		pr_err("Failed to open %s, err %d\n", trace_file_name, errno);
283 		return -3;
284 	}
285 
286 	pr_info("trace data file %s created for %s\n",
287 		trace_file_name, reader->dev_name);
288 
289 	if (pthread_create(&reader->thrd, NULL,
290 			   (void *)&reader_fn, &reader->param)) {
291 		pr_err("failed to create reader thread, %d\n", dev_id);
292 		return -4;
293 	}
294 
295 	return 0;
296 }
297 
destory_reader(reader_struct * reader)298 static void destory_reader(reader_struct * reader)
299 {
300 	if (reader->thrd) {
301 		pthread_cancel(reader->thrd);
302 		if (pthread_join(reader->thrd, NULL) != 0)
303 			pr_err("failed to cancel thread[%lu]\n", reader->thrd);
304 		else
305 			reader->thrd = 0;
306 	}
307 
308 	if (reader->param.sbuf) {
309 		munmap(reader->param.sbuf, MMAP_SIZE);
310 		reader->param.sbuf = NULL;
311 	}
312 
313 	if (reader->dev_fd) {
314 		close(reader->dev_fd);
315 		reader->dev_fd = 0;
316 	}
317 
318 	if (reader->param.trace_fd) {
319 		close(reader->param.trace_fd);
320 	}
321 }
322 
handle_on_exit(void)323 static void handle_on_exit(void)
324 {
325 	uint32_t dev_id;
326 
327 	/* if nothing to release */
328 	if (!(flags & FLAG_TO_REL))
329 		return;
330 
331 	pr_info("exiting - to release resources...\n");
332 
333 	foreach_dev(dev_id) {
334 		if (numa_bitmask_isbitset(cpu_bitmask, dev_id))
335 			destory_reader(&reader[dev_id]);
336 	}
337 }
338 
signal_exit_handler(int sig)339 static void signal_exit_handler(int sig)
340 {
341 	pr_info("exit on signal %d\n", sig);
342 	exit(0);
343 }
344 
main(int argc,char * argv[])345 int main(int argc, char *argv[])
346 {
347 	uint32_t dev_id = 0;
348 	int err;
349 
350 	/* parse options */
351 	if (parse_opt(argc, argv))
352 		exit(EXIT_FAILURE);
353 
354 	dev_cnt = get_dev_cnt();
355 	if (dev_cnt == 0) {
356 		pr_err("Failed to find acrn trace devices, please check whether module acrn_trace is inserted\n");
357 		exit(EXIT_FAILURE);
358 	}
359 
360 	/* if we don't set the -a option or set it by mistake, capture the trace on all possible dev */
361 	if (!cpu_bitmask) {
362 		/* numa_bitmask_alloc will cause the process exiting on failure */
363 		cpu_bitmask = numa_bitmask_alloc(dev_cnt);
364 		foreach_dev(dev_id)
365 			numa_bitmask_setbit(cpu_bitmask, dev_id);
366 	}
367 
368 	reader = calloc(1, sizeof(reader_struct) * dev_cnt);
369 	if (!reader) {
370 		pr_err("Failed to allocate reader memory\n");
371 		exit(EXIT_FAILURE);
372 	}
373 
374 	/* create dir for trace file */
375 	if (create_trace_file_dir(trace_file_dir)) {
376 		pr_err("Failed to create dir for trace files\n");
377 		exit(EXIT_FAILURE);
378 	}
379 
380 	/* Set timer if timeout configured */
381 	if (timeout) {
382 		err = init_timer(timeout);
383 		if (err < 0) {
384 			pr_err("Failed to set timer\n");
385 			exit(EXIT_FAILURE);
386 		}
387 	}
388 
389 	atexit(handle_on_exit);
390 
391 	/* acquair res for each trace dev */
392 	flags |= FLAG_TO_REL;
393 	foreach_dev(dev_id) {
394 		if (numa_bitmask_isbitset(cpu_bitmask, dev_id))
395 			if (create_reader(&reader[dev_id], dev_id) < 0)
396 				goto out_free;
397 	}
398 
399 	/* for kill exit handling */
400 	signal(SIGTERM, signal_exit_handler);
401 	signal(SIGINT, signal_exit_handler);
402 
403 	/* wait for user input to stop */
404 	printf("q <enter> to quit:\n");
405 	while (!exiting && getchar() != 'q')
406 		printf("q <enter> to quit:\n");
407 
408  out_free:
409 	foreach_dev(dev_id) {
410 		if (numa_bitmask_isbitset(cpu_bitmask, dev_id))
411 			destory_reader(&reader[dev_id]);
412 	}
413 
414 	free(reader);
415 	flags &= ~FLAG_TO_REL;
416 
417 	return EXIT_SUCCESS;
418 }
419