/* * get_llong_from_str - get a long long int from a string
*/ longlong get_llong_from_str(char *start)
{ longlong value; char *end;
errno = 0;
value = strtoll(start, &end, 10); if (errno || start == end) return -1;
return value;
}
/* * get_duration - fill output with a human readable duration since start_time
*/ void get_duration(time_t start_time, char *output, int output_size)
{
time_t now = time(NULL); struct tm *tm_info;
time_t duration;
/* * parse_cpu_set - parse a cpu_list filling cpu_set_t argument * * 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.
*/ int parse_cpu_set(char *cpu_list, cpu_set_t *set)
{ constchar *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) goto err;
while (isdigit(*p))
p++; if (*p == '-') {
p++;
end_cpu = atoi(p); if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus) goto err; while (isdigit(*p))
p++;
} else
end_cpu = cpu;
if (cpu == end_cpu) {
debug_msg("cpu_set: adding cpu %d\n", cpu);
CPU_SET(cpu, set);
} else { for (i = cpu; i <= end_cpu; i++) {
debug_msg("cpu_set: adding cpu %d\n", i);
CPU_SET(i, set);
}
}
if (*p == ',')
p++;
}
return 0;
err:
debug_msg("Error parsing the cpu set %s\n", cpu_list); return 1;
}
/* * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
*/ long parse_seconds_duration(char *val)
{ char *end; long t;
t = strtol(val, &end, 10);
if (end) { switch (*end) { case's': case'S': break; case'm': case'M':
t *= 60; break; case'h': case'H':
t *= 60 * 60; break;
case'd': case'D':
t *= 24 * 60 * 60; break;
}
}
return t;
}
/* * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
*/ long parse_ns_duration(char *val)
{ char *end; long t;
int __set_sched_attr(int pid, struct sched_attr *attr)
{ int flags = 0; int retval;
retval = syscall_sched_setattr(pid, attr, flags); if (retval < 0) {
err_msg("Failed to set sched attributes to the pid %d: %s\n",
pid, strerror(errno)); return 1;
}
return 0;
}
/* * procfs_is_workload_pid - check if a procfs entry contains a comm_prefix* comm * * Check if the procfs entry is a directory of a process, and then check if the * process has a comm with the prefix set in char *comm_prefix. As the * current users of this function only check for kernel threads, there is no * need to check for the threads for the process. * * Return: True if the proc_entry contains a comm file with comm_prefix*. * Otherwise returns false.
*/ staticint procfs_is_workload_pid(constchar *comm_prefix, struct dirent *proc_entry)
{ char buffer[MAX_PATH]; int comm_fd, retval; char *t_name;
if (proc_entry->d_type != DT_DIR) return 0;
if (*proc_entry->d_name == '.') return 0;
/* check if the string is a pid */ for (t_name = proc_entry->d_name; t_name; t_name++) { if (!isdigit(*t_name)) break;
}
retval = strncmp(comm_prefix, buffer, strlen(comm_prefix)); if (retval) return 0;
/* comm already have \n */
debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer);
return 1;
}
/* * set_comm_sched_attr - set sched params to threads starting with char *comm_prefix * * This function uses procfs to list the currently running threads and then set the * sched_attr *attr to the threads that start with char *comm_prefix. It is * mainly used to set the priority to the kernel threads created by the * tracers.
*/ int set_comm_sched_attr(constchar *comm_prefix, struct sched_attr *attr)
{ struct dirent *proc_entry;
DIR *procfs; int retval;
if (strlen(comm_prefix) >= MAX_PATH) {
err_msg("Command prefix is too long: %d < strlen(%s)\n",
MAX_PATH, comm_prefix); return 1;
}
procfs = opendir("/proc"); if (!procfs) {
err_msg("Could not open procfs\n"); return 1;
}
while ((proc_entry = readdir(procfs))) {
retval = procfs_is_workload_pid(comm_prefix, proc_entry); if (!retval) continue;
/* procfs_is_workload_pid confirmed it is a pid */
retval = __set_sched_attr(atoi(proc_entry->d_name), attr); if (retval) {
err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name); goto out_err;
}
debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name);
} return 0;
out_err:
closedir(procfs); return 1;
}
#define INVALID_VAL (~0L) staticlong get_long_ns_after_colon(char *start)
{ long val = INVALID_VAL;
/* find the ":" */
start = strstr(start, ":"); if (!start) return -1;
/* skip ":" */
start++;
val = parse_ns_duration(start);
return val;
}
staticlong get_long_after_colon(char *start)
{ long val = INVALID_VAL;
/* find the ":" */
start = strstr(start, ":"); if (!start) return -1;
/* skip ":" */
start++;
val = get_llong_from_str(start);
return val;
}
/* * parse priority in the format: * SCHED_OTHER: * o:<prio> * O:<prio> * SCHED_RR: * r:<prio> * R:<prio> * SCHED_FIFO: * f:<prio> * F:<prio> * SCHED_DEADLINE: * d:runtime:period * D:runtime:period
*/ int parse_prio(char *arg, struct sched_attr *sched_param)
{ long prio; long runtime; long period;
/* * set_cpu_dma_latency - set the /dev/cpu_dma_latecy * * This is used to reduce the exit from idle latency. The value * will be reset once the file descriptor of /dev/cpu_dma_latecy * is closed. * * Return: the /dev/cpu_dma_latecy file descriptor
*/ int set_cpu_dma_latency(int32_t latency)
{ int retval; int fd;
/* * save_cpu_idle_state_disable - save disable for all idle states of a cpu * * Saves the current disable of all idle states of a cpu, to be subsequently * restored via restore_cpu_idle_disable_state. * * Return: idle state count on success, negative on error
*/ int save_cpu_idle_disable_state(unsignedint cpu)
{ unsignedint nr_states; unsignedint state; int disabled; int nr_cpus;
nr_states = cpuidle_state_count(cpu);
if (nr_states == 0) return 0;
if (saved_cpu_idle_disable_state == NULL) {
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsignedint *)); if (!saved_cpu_idle_disable_state) return -1;
}
saved_cpu_idle_disable_state[cpu] = calloc(nr_states, sizeof(unsignedint)); if (!saved_cpu_idle_disable_state[cpu]) return -1;
saved_cpu_idle_disable_state_alloc_ctr++;
for (state = 0; state < nr_states; state++) {
disabled = cpuidle_is_state_disabled(cpu, state); if (disabled < 0) return disabled;
saved_cpu_idle_disable_state[cpu][state] = disabled;
}
return nr_states;
}
/* * restore_cpu_idle_disable_state - restore disable for all idle states of a cpu * * Restores the current disable state of all idle states of a cpu that was * previously saved by save_cpu_idle_disable_state. * * Return: idle state count on success, negative on error
*/ int restore_cpu_idle_disable_state(unsignedint cpu)
{ unsignedint nr_states; unsignedint state; int disabled; int result;
nr_states = cpuidle_state_count(cpu);
if (nr_states == 0) return 0;
if (!saved_cpu_idle_disable_state) return -1;
for (state = 0; state < nr_states; state++) { if (!saved_cpu_idle_disable_state[cpu]) return -1;
disabled = saved_cpu_idle_disable_state[cpu][state];
result = cpuidle_state_disable(cpu, state, disabled); if (result < 0) return result;
}
/* * free_cpu_idle_disable_states - free saved idle state disable for all cpus * * Frees the memory used for storing cpu idle state disable for all cpus * and states. * * Normally, the memory is freed automatically in * restore_cpu_idle_disable_state; this is mostly for cleaning up after an * error.
*/ 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;
}
/* * set_deepest_cpu_idle_state - limit idle state of cpu * * Disables all idle states deeper than the one given in * deepest_state (assuming states with higher number are deeper). * * This is used to reduce the exit from idle latency. Unlike * set_cpu_dma_latency, it can disable idle states per cpu. * * Return: idle state count on success, negative on error
*/ int set_deepest_cpu_idle_state(unsignedint cpu, unsignedint deepest_state)
{ unsignedint nr_states; unsignedint state; int result;
nr_states = cpuidle_state_count(cpu);
for (state = deepest_state + 1; state < nr_states; state++) {
result = cpuidle_state_disable(cpu, state, 1); if (result < 0) return result;
}
/* * find_mount - find a the mount point of a given fs * * Returns 0 if mount is not found, otherwise return 1 and fill mp * with the mount point.
*/ staticconstint find_mount(constchar *fs, char *mp, int sizeof_mp)
{ char mount_point[MAX_PATH+1]; char type[100]; int found = 0;
FILE *fp;
fp = fopen("/proc/mounts", "r"); if (!fp) return 0;
while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n", mount_point, type) == 2) { if (strcmp(type, fs) == 0) {
found = 1; break;
}
}
fclose(fp);
debug_msg("Fs %s found at %s\n", fs, mp); return 1;
}
/* * get_self_cgroup - get the current thread cgroup path * * Parse /proc/$$/cgroup file to get the thread's cgroup. As an example of line to parse: * * 0::/user.slice/user-0.slice/session-3.scope'\n' * * This function is interested in the content after the second : and before the '\n'. * * Returns 1 if a string was found, 0 otherwise.
*/ staticint get_self_cgroup(char *self_cg, int sizeof_self_cg)
{ char path[MAX_PATH], *start; int fd, retval;
/* Swap '\n' with '\0' */
start = strstr(self_cg, "\n");
/* there must be '\n' */ if (!start) return 0;
/* ok, it found a string after the second : and before the \n */
*start = '\0';
return 1;
}
/* * set_comm_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, constchar *cgroup)
{ char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; char cgroup_procs[MAX_PATH]; char pid_str[24]; int retval; int cg_fd;
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);
}
cg_fd = open(cgroup_procs, O_RDWR); if (cg_fd < 0) return 0;
snprintf(pid_str, sizeof(pid_str), "%d\n", pid);
retval = write(cg_fd, pid_str, strlen(pid_str)); if (retval < 0)
err_msg("Error setting cgroup attributes for pid:%s - %s\n",
pid_str, strerror(errno)); else
debug_msg("Set cgroup attributes for pid:%s\n", pid_str);
close(cg_fd);
return (retval >= 0);
}
/** * set_comm_cgroup - Set cgroup to threads starting with char *comm_prefix * * 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_comm_cgroup(constchar *comm_prefix, constchar *cgroup)
{ char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; char cgroup_procs[MAX_PATH]; struct dirent *proc_entry;
DIR *procfs; int retval; int cg_fd;
if (strlen(comm_prefix) >= MAX_PATH) {
err_msg("Command prefix is too long: %d < strlen(%s)\n",
MAX_PATH, comm_prefix); 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);
}
/** * auto_house_keeping - Automatically move rtla out of measurement threads * * Try to move rtla away from the tracer, if possible. * * Returns 1 on success, 0 otherwise.
*/ int auto_house_keeping(cpu_set_t *monitored_cpus)
{
cpu_set_t rtla_cpus, house_keeping_cpus; int retval;
/* first get the CPUs in which rtla can actually run. */
retval = sched_getaffinity(getpid(), sizeof(rtla_cpus), &rtla_cpus); if (retval == -1) {
debug_msg("Could not get rtla affinity, rtla might run with the threads!\n"); return 0;
}
/* then check if the existing setup is already good. */
CPU_AND(&house_keeping_cpus, &rtla_cpus, monitored_cpus); if (!CPU_COUNT(&house_keeping_cpus)) {
debug_msg("rtla and the monitored CPUs do not share CPUs.");
debug_msg("Skipping auto house-keeping\n"); return 1;
}
/* remove the intersection */
CPU_XOR(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
/* get only those that rtla can run */
CPU_AND(&house_keeping_cpus, &house_keeping_cpus, &rtla_cpus);
/* is there any cpu left? */ if (!CPU_COUNT(&house_keeping_cpus)) {
debug_msg("Could not find any CPU for auto house-keeping\n"); return 0;
}
retval = sched_setaffinity(getpid(), sizeof(house_keeping_cpus), &house_keeping_cpus); if (retval == -1) {
debug_msg("Could not set affinity for auto house-keeping\n"); return 0;
}
debug_msg("rtla automatically moved to an auto house-keeping cpu set\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.