xref: /linux/tools/tracing/rtla/src/utils.c (revision 009a8e681fb003f38dd57a640e11ed826740b5c1)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
4  */
5 
6 #define _GNU_SOURCE
7 #ifdef HAVE_LIBCPUPOWER_SUPPORT
8 #include <cpuidle.h>
9 #endif /* HAVE_LIBCPUPOWER_SUPPORT */
10 #include <dirent.h>
11 #include <stdarg.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <sched.h>
19 #include <stdio.h>
20 #include <limits.h>
21 
22 #include "common.h"
23 
24 #define MAX_MSG_LENGTH	1024
25 int config_debug;
26 
27 /*
28  * err_msg - print an error message to the stderr
29  */
30 void err_msg(const char *fmt, ...)
31 {
32 	char message[MAX_MSG_LENGTH];
33 	va_list ap;
34 
35 	va_start(ap, fmt);
36 	vsnprintf(message, sizeof(message), fmt, ap);
37 	va_end(ap);
38 
39 	fprintf(stderr, "%s", message);
40 }
41 
42 /*
43  * debug_msg - print a debug message to stderr if debug is set
44  */
45 void debug_msg(const char *fmt, ...)
46 {
47 	char message[MAX_MSG_LENGTH];
48 	va_list ap;
49 
50 	if (!config_debug)
51 		return;
52 
53 	va_start(ap, fmt);
54 	vsnprintf(message, sizeof(message), fmt, ap);
55 	va_end(ap);
56 
57 	fprintf(stderr, "%s", message);
58 }
59 
60 /*
61  * fatal - print an error message and EOL to stderr and exit with ERROR
62  */
63 void fatal(const char *fmt, ...)
64 {
65 	va_list ap;
66 
67 	va_start(ap, fmt);
68 	vfprintf(stderr, fmt, ap);
69 	va_end(ap);
70 	fprintf(stderr, "\n");
71 
72 	exit(ERROR);
73 }
74 
75 /*
76  * get_llong_from_str - get a long long int from a string
77  */
78 long long get_llong_from_str(char *start)
79 {
80 	long long value;
81 	char *end;
82 
83 	errno = 0;
84 	value = strtoll(start, &end, 10);
85 	if (errno || start == end)
86 		return -1;
87 
88 	return value;
89 }
90 
91 /*
92  * get_duration - fill output with a human readable duration since start_time
93  */
94 void get_duration(time_t start_time, char *output, int output_size)
95 {
96 	time_t now = time(NULL);
97 	struct tm *tm_info;
98 	time_t duration;
99 
100 	duration = difftime(now, start_time);
101 	tm_info = gmtime(&duration);
102 
103 	snprintf(output, output_size, "%3d %02d:%02d:%02d",
104 			tm_info->tm_yday,
105 			tm_info->tm_hour,
106 			tm_info->tm_min,
107 			tm_info->tm_sec);
108 }
109 
110 /*
111  * parse_cpu_set - parse a cpu_list filling cpu_set_t argument
112  *
113  * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set
114  * filling cpu_set_t argument.
115  *
116  * Returns 0 on success, 1 otherwise.
117  */
118 int parse_cpu_set(char *cpu_list, cpu_set_t *set)
119 {
120 	const char *p;
121 	int end_cpu;
122 	int cpu;
123 	int i;
124 
125 	CPU_ZERO(set);
126 
127 	for (p = cpu_list; *p; ) {
128 		cpu = atoi(p);
129 		if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
130 			goto err;
131 
132 		while (isdigit(*p))
133 			p++;
134 		if (*p == '-') {
135 			p++;
136 			end_cpu = atoi(p);
137 			if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
138 				goto err;
139 			while (isdigit(*p))
140 				p++;
141 		} else
142 			end_cpu = cpu;
143 
144 		if (cpu == end_cpu) {
145 			debug_msg("cpu_set: adding cpu %d\n", cpu);
146 			CPU_SET(cpu, set);
147 		} else {
148 			for (i = cpu; i <= end_cpu; i++) {
149 				debug_msg("cpu_set: adding cpu %d\n", i);
150 				CPU_SET(i, set);
151 			}
152 		}
153 
154 		if (*p == ',')
155 			p++;
156 	}
157 
158 	return 0;
159 err:
160 	debug_msg("Error parsing the cpu set %s\n", cpu_list);
161 	return 1;
162 }
163 
164 /*
165  * parse_stack_format - parse the stack format
166  *
167  * Return: the stack format on success, -1 otherwise.
168  */
169 int parse_stack_format(char *arg)
170 {
171 	if (!strcmp(arg, "truncate"))
172 		return STACK_FORMAT_TRUNCATE;
173 	if (!strcmp(arg, "skip"))
174 		return STACK_FORMAT_SKIP;
175 	if (!strcmp(arg, "full"))
176 		return STACK_FORMAT_FULL;
177 
178 	debug_msg("Error parsing the stack format %s\n", arg);
179 	return -1;
180 }
181 
182 /*
183  * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
184  */
185 long parse_seconds_duration(char *val)
186 {
187 	char *end;
188 	long t;
189 
190 	t = strtol(val, &end, 10);
191 
192 	if (end) {
193 		switch (*end) {
194 		case 's':
195 		case 'S':
196 			break;
197 		case 'm':
198 		case 'M':
199 			t *= 60;
200 			break;
201 		case 'h':
202 		case 'H':
203 			t *= 60 * 60;
204 			break;
205 
206 		case 'd':
207 		case 'D':
208 			t *= 24 * 60 * 60;
209 			break;
210 		}
211 	}
212 
213 	return t;
214 }
215 
216 /*
217  * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
218  */
219 long parse_ns_duration(char *val)
220 {
221 	char *end;
222 	long t;
223 
224 	t = strtol(val, &end, 10);
225 
226 	if (end) {
227 		if (!strncmp(end, "ns", 2)) {
228 			return t;
229 		} else if (!strncmp(end, "us", 2)) {
230 			t *= 1000;
231 			return t;
232 		} else if (!strncmp(end, "ms", 2)) {
233 			t *= 1000 * 1000;
234 			return t;
235 		} else if (!strncmp(end, "s", 1)) {
236 			t *= 1000 * 1000 * 1000;
237 			return t;
238 		}
239 		return -1;
240 	}
241 
242 	return t;
243 }
244 
245 /*
246  * This is a set of helper functions to use SCHED_DEADLINE.
247  */
248 #ifndef __NR_sched_setattr
249 # ifdef __x86_64__
250 #  define __NR_sched_setattr	314
251 # elif __i386__
252 #  define __NR_sched_setattr	351
253 # elif __arm__
254 #  define __NR_sched_setattr	380
255 # elif __aarch64__ || __riscv
256 #  define __NR_sched_setattr	274
257 # elif __powerpc__
258 #  define __NR_sched_setattr	355
259 # elif __s390x__
260 #  define __NR_sched_setattr	345
261 # elif __loongarch__
262 #  define __NR_sched_setattr	274
263 # endif
264 #endif
265 
266 #define SCHED_DEADLINE		6
267 
268 static inline int syscall_sched_setattr(pid_t pid, const struct sched_attr *attr,
269 				unsigned int flags) {
270 	return syscall(__NR_sched_setattr, pid, attr, flags);
271 }
272 
273 int __set_sched_attr(int pid, struct sched_attr *attr)
274 {
275 	int flags = 0;
276 	int retval;
277 
278 	retval = syscall_sched_setattr(pid, attr, flags);
279 	if (retval < 0) {
280 		err_msg("Failed to set sched attributes to the pid %d: %s\n",
281 			pid, strerror(errno));
282 		return 1;
283 	}
284 
285 	return 0;
286 }
287 
288 /*
289  * procfs_is_workload_pid - check if a procfs entry contains a comm_prefix* comm
290  *
291  * Check if the procfs entry is a directory of a process, and then check if the
292  * process has a comm with the prefix set in char *comm_prefix. As the
293  * current users of this function only check for kernel threads, there is no
294  * need to check for the threads for the process.
295  *
296  * Return: True if the proc_entry contains a comm file with comm_prefix*.
297  * Otherwise returns false.
298  */
299 static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_entry)
300 {
301 	char buffer[MAX_PATH];
302 	int comm_fd, retval;
303 	char *t_name;
304 
305 	if (proc_entry->d_type != DT_DIR)
306 		return 0;
307 
308 	if (*proc_entry->d_name == '.')
309 		return 0;
310 
311 	/* check if the string is a pid */
312 	for (t_name = proc_entry->d_name; t_name; t_name++) {
313 		if (!isdigit(*t_name))
314 			break;
315 	}
316 
317 	if (*t_name != '\0')
318 		return 0;
319 
320 	snprintf(buffer, MAX_PATH, "/proc/%s/comm", proc_entry->d_name);
321 	comm_fd = open(buffer, O_RDONLY);
322 	if (comm_fd < 0)
323 		return 0;
324 
325 	memset(buffer, 0, MAX_PATH);
326 	retval = read(comm_fd, buffer, MAX_PATH);
327 
328 	close(comm_fd);
329 
330 	if (retval <= 0)
331 		return 0;
332 
333 	buffer[MAX_PATH-1] = '\0';
334 	retval = strncmp(comm_prefix, buffer, strlen(comm_prefix));
335 	if (retval)
336 		return 0;
337 
338 	/* comm already have \n */
339 	debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer);
340 
341 	return 1;
342 }
343 
344 /*
345  * set_comm_sched_attr - set sched params to threads starting with char *comm_prefix
346  *
347  * This function uses procfs to list the currently running threads and then set the
348  * sched_attr *attr to the threads that start with char *comm_prefix. It is
349  * mainly used to set the priority to the kernel threads created by the
350  * tracers.
351  */
352 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
353 {
354 	struct dirent *proc_entry;
355 	DIR *procfs;
356 	int retval;
357 	int pid;
358 
359 	if (strlen(comm_prefix) >= MAX_PATH) {
360 		err_msg("Command prefix is too long: %d < strlen(%s)\n",
361 			MAX_PATH, comm_prefix);
362 		return 1;
363 	}
364 
365 	procfs = opendir("/proc");
366 	if (!procfs) {
367 		err_msg("Could not open procfs\n");
368 		return 1;
369 	}
370 
371 	while ((proc_entry = readdir(procfs))) {
372 
373 		retval = procfs_is_workload_pid(comm_prefix, proc_entry);
374 		if (!retval)
375 			continue;
376 
377 		if (strtoi(proc_entry->d_name, &pid)) {
378 			err_msg("'%s' is not a valid pid", proc_entry->d_name);
379 			goto out_err;
380 		}
381 		/* procfs_is_workload_pid confirmed it is a pid */
382 		retval = __set_sched_attr(pid, attr);
383 		if (retval) {
384 			err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name);
385 			goto out_err;
386 		}
387 
388 		debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name);
389 	}
390 	return 0;
391 
392 out_err:
393 	closedir(procfs);
394 	return 1;
395 }
396 
397 #define INVALID_VAL	(~0L)
398 static long get_long_ns_after_colon(char *start)
399 {
400 	long val = INVALID_VAL;
401 
402 	/* find the ":" */
403 	start = strstr(start, ":");
404 	if (!start)
405 		return -1;
406 
407 	/* skip ":" */
408 	start++;
409 	val = parse_ns_duration(start);
410 
411 	return val;
412 }
413 
414 static long get_long_after_colon(char *start)
415 {
416 	long val = INVALID_VAL;
417 
418 	/* find the ":" */
419 	start = strstr(start, ":");
420 	if (!start)
421 		return -1;
422 
423 	/* skip ":" */
424 	start++;
425 	val = get_llong_from_str(start);
426 
427 	return val;
428 }
429 
430 /*
431  * parse priority in the format:
432  * SCHED_OTHER:
433  *		o:<prio>
434  *		O:<prio>
435  * SCHED_RR:
436  *		r:<prio>
437  *		R:<prio>
438  * SCHED_FIFO:
439  *		f:<prio>
440  *		F:<prio>
441  * SCHED_DEADLINE:
442  *		d:runtime:period
443  *		D:runtime:period
444  */
445 int parse_prio(char *arg, struct sched_attr *sched_param)
446 {
447 	long prio;
448 	long runtime;
449 	long period;
450 
451 	memset(sched_param, 0, sizeof(*sched_param));
452 	sched_param->size = sizeof(*sched_param);
453 
454 	switch (arg[0]) {
455 	case 'd':
456 	case 'D':
457 		/* d:runtime:period */
458 		if (strlen(arg) < 4)
459 			return -1;
460 
461 		runtime = get_long_ns_after_colon(arg);
462 		if (runtime == INVALID_VAL)
463 			return -1;
464 
465 		period = get_long_ns_after_colon(&arg[2]);
466 		if (period == INVALID_VAL)
467 			return -1;
468 
469 		if (runtime > period)
470 			return -1;
471 
472 		sched_param->sched_policy   = SCHED_DEADLINE;
473 		sched_param->sched_runtime  = runtime;
474 		sched_param->sched_deadline = period;
475 		sched_param->sched_period   = period;
476 		break;
477 	case 'f':
478 	case 'F':
479 		/* f:prio */
480 		prio = get_long_after_colon(arg);
481 		if (prio == INVALID_VAL)
482 			return -1;
483 
484 		if (prio < sched_get_priority_min(SCHED_FIFO))
485 			return -1;
486 		if (prio > sched_get_priority_max(SCHED_FIFO))
487 			return -1;
488 
489 		sched_param->sched_policy   = SCHED_FIFO;
490 		sched_param->sched_priority = prio;
491 		break;
492 	case 'r':
493 	case 'R':
494 		/* r:prio */
495 		prio = get_long_after_colon(arg);
496 		if (prio == INVALID_VAL)
497 			return -1;
498 
499 		if (prio < sched_get_priority_min(SCHED_RR))
500 			return -1;
501 		if (prio > sched_get_priority_max(SCHED_RR))
502 			return -1;
503 
504 		sched_param->sched_policy   = SCHED_RR;
505 		sched_param->sched_priority = prio;
506 		break;
507 	case 'o':
508 	case 'O':
509 		/* o:prio */
510 		prio = get_long_after_colon(arg);
511 		if (prio == INVALID_VAL)
512 			return -1;
513 
514 		if (prio < MIN_NICE)
515 			return -1;
516 		if (prio > MAX_NICE)
517 			return -1;
518 
519 		sched_param->sched_policy   = SCHED_OTHER;
520 		sched_param->sched_nice = prio;
521 		break;
522 	default:
523 		return -1;
524 	}
525 	return 0;
526 }
527 
528 /*
529  * set_cpu_dma_latency - set the /dev/cpu_dma_latecy
530  *
531  * This is used to reduce the exit from idle latency. The value
532  * will be reset once the file descriptor of /dev/cpu_dma_latecy
533  * is closed.
534  *
535  * Return: the /dev/cpu_dma_latecy file descriptor
536  */
537 int set_cpu_dma_latency(int32_t latency)
538 {
539 	int retval;
540 	int fd;
541 
542 	fd = open("/dev/cpu_dma_latency", O_RDWR);
543 	if (fd < 0) {
544 		err_msg("Error opening /dev/cpu_dma_latency\n");
545 		return -1;
546 	}
547 
548 	retval = write(fd, &latency, 4);
549 	if (retval < 1) {
550 		err_msg("Error setting /dev/cpu_dma_latency\n");
551 		close(fd);
552 		return -1;
553 	}
554 
555 	debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);
556 
557 	return fd;
558 }
559 
560 #ifdef HAVE_LIBCPUPOWER_SUPPORT
561 static unsigned int **saved_cpu_idle_disable_state;
562 static size_t saved_cpu_idle_disable_state_alloc_ctr;
563 
564 /*
565  * save_cpu_idle_state_disable - save disable for all idle states of a cpu
566  *
567  * Saves the current disable of all idle states of a cpu, to be subsequently
568  * restored via restore_cpu_idle_disable_state.
569  *
570  * Return: idle state count on success, negative on error
571  */
572 int save_cpu_idle_disable_state(unsigned int cpu)
573 {
574 	unsigned int nr_states;
575 	unsigned int state;
576 	int disabled;
577 
578 	nr_states = cpuidle_state_count(cpu);
579 
580 	if (nr_states == 0)
581 		return 0;
582 
583 	if (saved_cpu_idle_disable_state == NULL) {
584 		saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsigned int *));
585 		if (!saved_cpu_idle_disable_state)
586 			return -1;
587 	}
588 
589 	saved_cpu_idle_disable_state[cpu] = calloc(nr_states, sizeof(unsigned int));
590 	if (!saved_cpu_idle_disable_state[cpu])
591 		return -1;
592 	saved_cpu_idle_disable_state_alloc_ctr++;
593 
594 	for (state = 0; state < nr_states; state++) {
595 		disabled = cpuidle_is_state_disabled(cpu, state);
596 		if (disabled < 0)
597 			return disabled;
598 		saved_cpu_idle_disable_state[cpu][state] = disabled;
599 	}
600 
601 	return nr_states;
602 }
603 
604 /*
605  * restore_cpu_idle_disable_state - restore disable for all idle states of a cpu
606  *
607  * Restores the current disable state of all idle states of a cpu that was
608  * previously saved by save_cpu_idle_disable_state.
609  *
610  * Return: idle state count on success, negative on error
611  */
612 int restore_cpu_idle_disable_state(unsigned int cpu)
613 {
614 	unsigned int nr_states;
615 	unsigned int state;
616 	int disabled;
617 	int result;
618 
619 	nr_states = cpuidle_state_count(cpu);
620 
621 	if (nr_states == 0)
622 		return 0;
623 
624 	if (!saved_cpu_idle_disable_state)
625 		return -1;
626 
627 	for (state = 0; state < nr_states; state++) {
628 		if (!saved_cpu_idle_disable_state[cpu])
629 			return -1;
630 		disabled = saved_cpu_idle_disable_state[cpu][state];
631 		result = cpuidle_state_disable(cpu, state, disabled);
632 		if (result < 0)
633 			return result;
634 	}
635 
636 	free(saved_cpu_idle_disable_state[cpu]);
637 	saved_cpu_idle_disable_state[cpu] = NULL;
638 	saved_cpu_idle_disable_state_alloc_ctr--;
639 	if (saved_cpu_idle_disable_state_alloc_ctr == 0) {
640 		free(saved_cpu_idle_disable_state);
641 		saved_cpu_idle_disable_state = NULL;
642 	}
643 
644 	return nr_states;
645 }
646 
647 /*
648  * free_cpu_idle_disable_states - free saved idle state disable for all cpus
649  *
650  * Frees the memory used for storing cpu idle state disable for all cpus
651  * and states.
652  *
653  * Normally, the memory is freed automatically in
654  * restore_cpu_idle_disable_state; this is mostly for cleaning up after an
655  * error.
656  */
657 void free_cpu_idle_disable_states(void)
658 {
659 	int cpu;
660 
661 	if (!saved_cpu_idle_disable_state)
662 		return;
663 
664 	for (cpu = 0; cpu < nr_cpus; cpu++) {
665 		free(saved_cpu_idle_disable_state[cpu]);
666 		saved_cpu_idle_disable_state[cpu] = NULL;
667 	}
668 
669 	free(saved_cpu_idle_disable_state);
670 	saved_cpu_idle_disable_state = NULL;
671 }
672 
673 /*
674  * set_deepest_cpu_idle_state - limit idle state of cpu
675  *
676  * Disables all idle states deeper than the one given in
677  * deepest_state (assuming states with higher number are deeper).
678  *
679  * This is used to reduce the exit from idle latency. Unlike
680  * set_cpu_dma_latency, it can disable idle states per cpu.
681  *
682  * Return: idle state count on success, negative on error
683  */
684 int set_deepest_cpu_idle_state(unsigned int cpu, unsigned int deepest_state)
685 {
686 	unsigned int nr_states;
687 	unsigned int state;
688 	int result;
689 
690 	nr_states = cpuidle_state_count(cpu);
691 
692 	for (state = deepest_state + 1; state < nr_states; state++) {
693 		result = cpuidle_state_disable(cpu, state, 1);
694 		if (result < 0)
695 			return result;
696 	}
697 
698 	return nr_states;
699 }
700 #endif /* HAVE_LIBCPUPOWER_SUPPORT */
701 
702 #define _STR(x) #x
703 #define STR(x) _STR(x)
704 
705 /*
706  * find_mount - find a the mount point of a given fs
707  *
708  * Returns 0 if mount is not found, otherwise return 1 and fill mp
709  * with the mount point.
710  */
711 static const int find_mount(const char *fs, char *mp, int sizeof_mp)
712 {
713 	char mount_point[MAX_PATH+1];
714 	char type[100];
715 	int found = 0;
716 	FILE *fp;
717 
718 	fp = fopen("/proc/mounts", "r");
719 	if (!fp)
720 		return 0;
721 
722 	while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n",	mount_point, type) == 2) {
723 		if (strcmp(type, fs) == 0) {
724 			found = 1;
725 			break;
726 		}
727 	}
728 	fclose(fp);
729 
730 	if (!found)
731 		return 0;
732 
733 	memset(mp, 0, sizeof_mp);
734 	strncpy(mp, mount_point, sizeof_mp - 1);
735 
736 	debug_msg("Fs %s found at %s\n", fs, mp);
737 	return 1;
738 }
739 
740 /*
741  * get_self_cgroup - get the current thread cgroup path
742  *
743  * Parse /proc/$$/cgroup file to get the thread's cgroup. As an example of line to parse:
744  *
745  * 0::/user.slice/user-0.slice/session-3.scope'\n'
746  *
747  * This function is interested in the content after the second : and before the '\n'.
748  *
749  * Returns 1 if a string was found, 0 otherwise.
750  */
751 static int get_self_cgroup(char *self_cg, int sizeof_self_cg)
752 {
753 	char path[MAX_PATH], *start;
754 	int fd, retval;
755 
756 	snprintf(path, MAX_PATH, "/proc/%d/cgroup", getpid());
757 
758 	fd = open(path, O_RDONLY);
759 	if (fd < 0)
760 		return 0;
761 
762 	memset(path, 0, sizeof(path));
763 	retval = read(fd, path, MAX_PATH);
764 
765 	close(fd);
766 
767 	if (retval <= 0)
768 		return 0;
769 
770 	path[MAX_PATH-1] = '\0';
771 	start = path;
772 
773 	start = strstr(start, ":");
774 	if (!start)
775 		return 0;
776 
777 	/* skip ":" */
778 	start++;
779 
780 	start = strstr(start, ":");
781 	if (!start)
782 		return 0;
783 
784 	/* skip ":" */
785 	start++;
786 
787 	if (strlen(start) >= sizeof_self_cg)
788 		return 0;
789 
790 	snprintf(self_cg, sizeof_self_cg, "%s", start);
791 
792 	/* Swap '\n' with '\0' */
793 	start = strstr(self_cg, "\n");
794 
795 	/* there must be '\n' */
796 	if (!start)
797 		return 0;
798 
799 	/* ok, it found a string after the second : and before the \n */
800 	*start = '\0';
801 
802 	return 1;
803 }
804 
805 /*
806  * open_cgroup_procs - Open the cgroup.procs file for the given cgroup
807  *
808  * If cgroup argument is not NULL, the cgroup.procs file for that cgroup
809  * will be opened. Otherwise, the cgroup of the calling, i.e., rtla, thread
810  * will be used.
811  *
812  * Supports cgroup v2.
813  *
814  * Returns the file descriptor on success, -1 otherwise.
815  */
816 static int open_cgroup_procs(const char *cgroup)
817 {
818 	char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
819 	char cgroup_procs[MAX_PATH];
820 	int retval;
821 	int cg_fd;
822 
823 	retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
824 	if (!retval) {
825 		err_msg("Did not find cgroupv2 mount point\n");
826 		return -1;
827 	}
828 
829 	if (!cgroup) {
830 		retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)],
831 				sizeof(cgroup_path) - strlen(cgroup_path));
832 		if (!retval) {
833 			err_msg("Did not find self cgroup\n");
834 			return -1;
835 		}
836 	} else {
837 		snprintf(&cgroup_path[strlen(cgroup_path)],
838 				sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup);
839 	}
840 
841 	snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path);
842 
843 	debug_msg("Using cgroup path at: %s\n", cgroup_procs);
844 
845 	cg_fd = open(cgroup_procs, O_RDWR);
846 	if (cg_fd < 0)
847 		return -1;
848 
849 	return cg_fd;
850 }
851 
852 /*
853  * set_pid_cgroup - Set cgroup to pid_t pid
854  *
855  * If cgroup argument is not NULL, the threads will move to the given cgroup.
856  * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
857  *
858  * Supports cgroup v2.
859  *
860  * Returns 1 on success, 0 otherwise.
861  */
862 int set_pid_cgroup(pid_t pid, const char *cgroup)
863 {
864 	char pid_str[24];
865 	int retval;
866 	int cg_fd;
867 
868 	cg_fd = open_cgroup_procs(cgroup);
869 	if (cg_fd < 0)
870 		return 0;
871 
872 	snprintf(pid_str, sizeof(pid_str), "%d\n", pid);
873 
874 	retval = write(cg_fd, pid_str, strlen(pid_str));
875 	if (retval < 0)
876 		err_msg("Error setting cgroup attributes for pid:%s - %s\n",
877 				pid_str, strerror(errno));
878 	else
879 		debug_msg("Set cgroup attributes for pid:%s\n", pid_str);
880 
881 	close(cg_fd);
882 
883 	return (retval >= 0);
884 }
885 
886 /**
887  * set_comm_cgroup - Set cgroup to threads starting with char *comm_prefix
888  *
889  * If cgroup argument is not NULL, the threads will move to the given cgroup.
890  * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
891  *
892  * Supports cgroup v2.
893  *
894  * Returns 1 on success, 0 otherwise.
895  */
896 int set_comm_cgroup(const char *comm_prefix, const char *cgroup)
897 {
898 	struct dirent *proc_entry;
899 	DIR *procfs;
900 	int retval;
901 	int cg_fd;
902 
903 	if (strlen(comm_prefix) >= MAX_PATH) {
904 		err_msg("Command prefix is too long: %d < strlen(%s)\n",
905 			MAX_PATH, comm_prefix);
906 		return 0;
907 	}
908 
909 	cg_fd = open_cgroup_procs(cgroup);
910 	if (cg_fd < 0)
911 		return 0;
912 
913 	procfs = opendir("/proc");
914 	if (!procfs) {
915 		err_msg("Could not open procfs\n");
916 		goto out_cg;
917 	}
918 
919 	while ((proc_entry = readdir(procfs))) {
920 
921 		retval = procfs_is_workload_pid(comm_prefix, proc_entry);
922 		if (!retval)
923 			continue;
924 
925 		retval = write(cg_fd, proc_entry->d_name, strlen(proc_entry->d_name));
926 		if (retval < 0) {
927 			err_msg("Error setting cgroup attributes for pid:%s - %s\n",
928 				proc_entry->d_name, strerror(errno));
929 			goto out_procfs;
930 		}
931 
932 		debug_msg("Set cgroup attributes for pid:%s\n", proc_entry->d_name);
933 	}
934 
935 	closedir(procfs);
936 	close(cg_fd);
937 	return 1;
938 
939 out_procfs:
940 	closedir(procfs);
941 out_cg:
942 	close(cg_fd);
943 	return 0;
944 }
945 
946 /**
947  * auto_house_keeping - Automatically move rtla out of measurement threads
948  *
949  * Try to move rtla away from the tracer, if possible.
950  *
951  * Returns 1 on success, 0 otherwise.
952  */
953 int auto_house_keeping(cpu_set_t *monitored_cpus)
954 {
955 	cpu_set_t rtla_cpus, house_keeping_cpus;
956 	int retval;
957 
958 	/* first get the CPUs in which rtla can actually run. */
959 	retval = sched_getaffinity(getpid(), sizeof(rtla_cpus), &rtla_cpus);
960 	if (retval == -1) {
961 		debug_msg("Could not get rtla affinity, rtla might run with the threads!\n");
962 		return 0;
963 	}
964 
965 	/* then check if the existing setup is already good. */
966 	CPU_AND(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
967 	if (!CPU_COUNT(&house_keeping_cpus)) {
968 		debug_msg("rtla and the monitored CPUs do not share CPUs.");
969 		debug_msg("Skipping auto house-keeping\n");
970 		return 1;
971 	}
972 
973 	/* remove the intersection */
974 	CPU_XOR(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
975 
976 	/* get only those that rtla can run */
977 	CPU_AND(&house_keeping_cpus, &house_keeping_cpus, &rtla_cpus);
978 
979 	/* is there any cpu left? */
980 	if (!CPU_COUNT(&house_keeping_cpus)) {
981 		debug_msg("Could not find any CPU for auto house-keeping\n");
982 		return 0;
983 	}
984 
985 	retval = sched_setaffinity(getpid(), sizeof(house_keeping_cpus), &house_keeping_cpus);
986 	if (retval == -1) {
987 		debug_msg("Could not set affinity for auto house-keeping\n");
988 		return 0;
989 	}
990 
991 	debug_msg("rtla automatically moved to an auto house-keeping cpu set\n");
992 
993 	return 1;
994 }
995 
996 /**
997  * parse_optional_arg - Parse optional argument value
998  *
999  * Parse optional argument value, which can be in the form of:
1000  * -sarg, -s/--long=arg, -s/--long arg
1001  *
1002  * Returns arg value if found, NULL otherwise.
1003  */
1004 char *parse_optional_arg(int argc, char **argv)
1005 {
1006 	if (optarg) {
1007 		if (optarg[0] == '=') {
1008 			/* skip the = */
1009 			return &optarg[1];
1010 		} else {
1011 			return optarg;
1012 		}
1013 	/* parse argument of form -s [arg] and --long [arg]*/
1014 	} else if (optind < argc && argv[optind][0] != '-') {
1015 		/* consume optind */
1016 		return argv[optind++];
1017 	} else {
1018 		return NULL;
1019 	}
1020 }
1021 
1022 /*
1023  * strtoi - convert string to integer with error checking
1024  *
1025  * Returns 0 on success, -1 if conversion fails or result is out of int range.
1026  */
1027 int strtoi(const char *s, int *res)
1028 {
1029 	char *end_ptr;
1030 	long lres;
1031 
1032 	if (!*s)
1033 		return -1;
1034 
1035 	errno = 0;
1036 	lres = strtol(s, &end_ptr, 0);
1037 	if (errno || *end_ptr || lres > INT_MAX || lres < INT_MIN)
1038 		return -1;
1039 
1040 	*res = (int) lres;
1041 	return 0;
1042 }
1043 
1044 static inline void fatal_alloc(void)
1045 {
1046 	fatal("Error allocating memory\n");
1047 }
1048 
1049 void *calloc_fatal(size_t n, size_t size)
1050 {
1051 	void *p = calloc(n, size);
1052 
1053 	if (!p)
1054 		fatal_alloc();
1055 
1056 	return p;
1057 }
1058 
1059 void *reallocarray_fatal(void *p, size_t n, size_t size)
1060 {
1061 	p = reallocarray(p, n, size);
1062 
1063 	if (!p)
1064 		fatal_alloc();
1065 
1066 	return p;
1067 }
1068 
1069 char *strdup_fatal(const char *s)
1070 {
1071 	char *p = strdup(s);
1072 
1073 	if (!p)
1074 		fatal_alloc();
1075 
1076 	return p;
1077 }
1078