diff options
Diffstat (limited to 'tools/tracing/rtla/src/utils.c')
| -rw-r--r-- | tools/tracing/rtla/src/utils.c | 254 |
1 files changed, 195 insertions, 59 deletions
diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 4995d35cf3ec..9cec5b3e02c8 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -17,8 +17,9 @@ #include <fcntl.h> #include <sched.h> #include <stdio.h> +#include <limits.h> -#include "utils.h" +#include "common.h" #define MAX_MSG_LENGTH 1024 int config_debug; @@ -57,6 +58,21 @@ void debug_msg(const char *fmt, ...) } /* + * fatal - print an error message and EOL to stderr and exit with ERROR + */ +void fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + exit(ERROR); +} + +/* * get_llong_from_str - get a long long int from a string */ long long get_llong_from_str(char *start) @@ -97,20 +113,17 @@ void get_duration(time_t start_time, char *output, int output_size) * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set * filling cpu_set_t argument. * - * Returns 1 on success, 0 otherwise. + * Returns 0 on success, 1 otherwise. */ int parse_cpu_set(char *cpu_list, cpu_set_t *set) { const char *p; int end_cpu; - int nr_cpus; int cpu; int i; CPU_ZERO(set); - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - for (p = cpu_list; *p; ) { cpu = atoi(p); if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus) @@ -149,6 +162,24 @@ err: } /* + * parse_stack_format - parse the stack format + * + * Return: the stack format on success, -1 otherwise. + */ +int parse_stack_format(char *arg) +{ + if (!strcmp(arg, "truncate")) + return STACK_FORMAT_TRUNCATE; + if (!strcmp(arg, "skip")) + return STACK_FORMAT_SKIP; + if (!strcmp(arg, "full")) + return STACK_FORMAT_FULL; + + debug_msg("Error parsing the stack format %s\n", arg); + return -1; +} + +/* * parse_duration - parse duration with s/m/h/d suffix converting it to seconds */ long parse_seconds_duration(char *val) @@ -183,6 +214,21 @@ long parse_seconds_duration(char *val) } /* + * match_time_unit - check if str starts with unit followed by end-of-string or ':' + * + * This allows the time unit parser to work both in standalone duration strings + * like "100ms" and in colon-delimited SCHED_DEADLINE specifications like + * "d:10ms:100ms", while still rejecting malformed input like "100msx". + */ +static bool match_time_unit(const char *str, const char *unit) +{ + size_t len = strlen(unit); + + return strncmp(str, unit, len) == 0 && + (str[len] == '\0' || str[len] == ':'); +} + +/* * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds */ long parse_ns_duration(char *val) @@ -193,15 +239,15 @@ long parse_ns_duration(char *val) t = strtol(val, &end, 10); if (end) { - if (!strncmp(end, "ns", 2)) { + if (match_time_unit(end, "ns")) { return t; - } else if (!strncmp(end, "us", 2)) { + } else if (match_time_unit(end, "us")) { t *= 1000; return t; - } else if (!strncmp(end, "ms", 2)) { + } else if (match_time_unit(end, "ms")) { t *= 1000 * 1000; return t; - } else if (!strncmp(end, "s", 1)) { + } else if (match_time_unit(end, "s")) { t *= 1000 * 1000 * 1000; return t; } @@ -227,6 +273,8 @@ long parse_ns_duration(char *val) # define __NR_sched_setattr 355 # elif __s390x__ # define __NR_sched_setattr 345 +# elif __loongarch__ +# define __NR_sched_setattr 274 # endif #endif @@ -276,7 +324,7 @@ static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_e return 0; /* check if the string is a pid */ - for (t_name = proc_entry->d_name; t_name; t_name++) { + for (t_name = proc_entry->d_name; *t_name; t_name++) { if (!isdigit(*t_name)) break; } @@ -297,8 +345,8 @@ static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_e if (retval <= 0) return 0; - retval = strncmp(comm_prefix, buffer, strlen(comm_prefix)); - if (retval) + buffer[MAX_PATH-1] = '\0'; + if (!str_has_prefix(buffer, comm_prefix)) return 0; /* comm already have \n */ @@ -320,6 +368,7 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr) struct dirent *proc_entry; DIR *procfs; int retval; + int pid; if (strlen(comm_prefix) >= MAX_PATH) { err_msg("Command prefix is too long: %d < strlen(%s)\n", @@ -339,20 +388,25 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr) if (!retval) continue; + if (strtoi(proc_entry->d_name, &pid)) { + err_msg("'%s' is not a valid pid", proc_entry->d_name); + retval = 1; + goto out; + } /* procfs_is_workload_pid confirmed it is a pid */ - retval = __set_sched_attr(atoi(proc_entry->d_name), attr); + retval = __set_sched_attr(pid, attr); if (retval) { err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name); - goto out_err; + goto out; } debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name); } - return 0; -out_err: + retval = 0; +out: closedir(procfs); - return 1; + return retval; } #define INVALID_VAL (~0L) @@ -535,7 +589,6 @@ int save_cpu_idle_disable_state(unsigned int cpu) unsigned int nr_states; unsigned int state; int disabled; - int nr_cpus; nr_states = cpuidle_state_count(cpu); @@ -543,7 +596,6 @@ int save_cpu_idle_disable_state(unsigned int cpu) return 0; if (saved_cpu_idle_disable_state == NULL) { - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsigned int *)); if (!saved_cpu_idle_disable_state) return -1; @@ -620,13 +672,10 @@ int restore_cpu_idle_disable_state(unsigned int cpu) void free_cpu_idle_disable_states(void) { int cpu; - int nr_cpus; if (!saved_cpu_idle_disable_state) return; - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - for (cpu = 0; cpu < nr_cpus; cpu++) { free(saved_cpu_idle_disable_state[cpu]); saved_cpu_idle_disable_state[cpu] = NULL; @@ -725,6 +774,7 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg) if (fd < 0) return 0; + memset(path, 0, sizeof(path)); retval = read(fd, path, MAX_PATH); close(fd); @@ -732,6 +782,7 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg) if (retval <= 0) return 0; + path[MAX_PATH-1] = '\0'; start = path; start = strstr(start, ":"); @@ -767,39 +818,42 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg) } /* - * set_comm_cgroup - Set cgroup to pid_t pid + * open_cgroup_procs - Open the cgroup.procs file for the given cgroup * - * If cgroup argument is not NULL, the threads will move to the given cgroup. - * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. + * If cgroup argument is not NULL, the cgroup.procs file for that cgroup + * will be opened. Otherwise, the cgroup of the calling, i.e., rtla, thread + * will be used. * * Supports cgroup v2. * - * Returns 1 on success, 0 otherwise. + * Returns the file descriptor on success, -1 otherwise. */ -int set_pid_cgroup(pid_t pid, const char *cgroup) +static int open_cgroup_procs(const char *cgroup) { char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; char cgroup_procs[MAX_PATH]; - char pid_str[24]; int retval; int cg_fd; + size_t cg_path_len; retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); if (!retval) { err_msg("Did not find cgroupv2 mount point\n"); - return 0; + return -1; } + cg_path_len = strlen(cgroup_path); + if (!cgroup) { - retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path)); + retval = get_self_cgroup(&cgroup_path[cg_path_len], + sizeof(cgroup_path) - cg_path_len); if (!retval) { err_msg("Did not find self cgroup\n"); - return 0; + return -1; } } else { - snprintf(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup); + snprintf(&cgroup_path[cg_path_len], + sizeof(cgroup_path) - cg_path_len, "%s/", cgroup); } snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path); @@ -808,6 +862,29 @@ int set_pid_cgroup(pid_t pid, const char *cgroup) cg_fd = open(cgroup_procs, O_RDWR); if (cg_fd < 0) + return -1; + + return cg_fd; +} + +/* + * set_pid_cgroup - Set cgroup to pid_t pid + * + * If cgroup argument is not NULL, the threads will move to the given cgroup. + * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. + * + * Supports cgroup v2. + * + * Returns 1 on success, 0 otherwise. + */ +int set_pid_cgroup(pid_t pid, const char *cgroup) +{ + char pid_str[24]; + int retval; + int cg_fd; + + cg_fd = open_cgroup_procs(cgroup); + if (cg_fd < 0) return 0; snprintf(pid_str, sizeof(pid_str), "%d\n", pid); @@ -836,8 +913,6 @@ int set_pid_cgroup(pid_t pid, const char *cgroup) */ int set_comm_cgroup(const char *comm_prefix, const char *cgroup) { - char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; - char cgroup_procs[MAX_PATH]; struct dirent *proc_entry; DIR *procfs; int retval; @@ -849,29 +924,7 @@ int set_comm_cgroup(const char *comm_prefix, const char *cgroup) return 0; } - retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); - if (!retval) { - err_msg("Did not find cgroupv2 mount point\n"); - return 0; - } - - if (!cgroup) { - retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path)); - if (!retval) { - err_msg("Did not find self cgroup\n"); - return 0; - } - } else { - snprintf(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup); - } - - snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path); - - debug_msg("Using cgroup path at: %s\n", cgroup_procs); - - cg_fd = open(cgroup_procs, O_RDWR); + cg_fd = open_cgroup_procs(cgroup); if (cg_fd < 0) return 0; @@ -957,3 +1010,86 @@ int auto_house_keeping(cpu_set_t *monitored_cpus) return 1; } + +/** + * parse_optional_arg - Parse optional argument value + * + * Parse optional argument value, which can be in the form of: + * -sarg, -s/--long=arg, -s/--long arg + * + * Returns arg value if found, NULL otherwise. + */ +char *parse_optional_arg(int argc, char **argv) +{ + if (optarg) { + if (optarg[0] == '=') { + /* skip the = */ + return &optarg[1]; + } else { + return optarg; + } + /* parse argument of form -s [arg] and --long [arg]*/ + } else if (optind < argc && argv[optind][0] != '-') { + /* consume optind */ + return argv[optind++]; + } else { + return NULL; + } +} + +/* + * strtoi - convert string to integer with error checking + * + * Returns 0 on success, -1 if conversion fails or result is out of int range. + */ +int strtoi(const char *s, int *res) +{ + char *end_ptr; + long lres; + + if (!*s) + return -1; + + errno = 0; + lres = strtol(s, &end_ptr, 0); + if (errno || *end_ptr || lres > INT_MAX || lres < INT_MIN) + return -1; + + *res = (int) lres; + return 0; +} + +static inline void fatal_alloc(void) +{ + fatal("Error allocating memory\n"); +} + +void *calloc_fatal(size_t n, size_t size) +{ + void *p = calloc(n, size); + + if (!p) + fatal_alloc(); + + return p; +} + +void *reallocarray_fatal(void *p, size_t n, size_t size) +{ + p = reallocarray(p, n, size); + + if (!p) + fatal_alloc(); + + return p; +} + +char *strdup_fatal(const char *s) +{ + char *p = strdup(s); + + if (!p) + fatal_alloc(); + + return p; +} |
