linux/tools/tracing/rtla/src/utils.c
<<
>>
Prefs
   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#include <proc/readproc.h>
   7#include <stdarg.h>
   8#include <stdlib.h>
   9#include <string.h>
  10#include <unistd.h>
  11#include <ctype.h>
  12#include <errno.h>
  13#include <sched.h>
  14#include <stdio.h>
  15
  16#include "utils.h"
  17
  18#define MAX_MSG_LENGTH  1024
  19int config_debug;
  20
  21/*
  22 * err_msg - print an error message to the stderr
  23 */
  24void err_msg(const char *fmt, ...)
  25{
  26        char message[MAX_MSG_LENGTH];
  27        va_list ap;
  28
  29        va_start(ap, fmt);
  30        vsnprintf(message, sizeof(message), fmt, ap);
  31        va_end(ap);
  32
  33        fprintf(stderr, "%s", message);
  34}
  35
  36/*
  37 * debug_msg - print a debug message to stderr if debug is set
  38 */
  39void debug_msg(const char *fmt, ...)
  40{
  41        char message[MAX_MSG_LENGTH];
  42        va_list ap;
  43
  44        if (!config_debug)
  45                return;
  46
  47        va_start(ap, fmt);
  48        vsnprintf(message, sizeof(message), fmt, ap);
  49        va_end(ap);
  50
  51        fprintf(stderr, "%s", message);
  52}
  53
  54/*
  55 * get_llong_from_str - get a long long int from a string
  56 */
  57long long get_llong_from_str(char *start)
  58{
  59        long long value;
  60        char *end;
  61
  62        errno = 0;
  63        value = strtoll(start, &end, 10);
  64        if (errno || start == end)
  65                return -1;
  66
  67        return value;
  68}
  69
  70/*
  71 * get_duration - fill output with a human readable duration since start_time
  72 */
  73void get_duration(time_t start_time, char *output, int output_size)
  74{
  75        time_t now = time(NULL);
  76        struct tm *tm_info;
  77        time_t duration;
  78
  79        duration = difftime(now, start_time);
  80        tm_info = gmtime(&duration);
  81
  82        snprintf(output, output_size, "%3d %02d:%02d:%02d",
  83                        tm_info->tm_yday,
  84                        tm_info->tm_hour,
  85                        tm_info->tm_min,
  86                        tm_info->tm_sec);
  87}
  88
  89/*
  90 * parse_cpu_list - parse a cpu_list filling a char vector with cpus set
  91 *
  92 * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set the char
  93 * in the monitored_cpus.
  94 *
  95 * XXX: convert to a bitmask.
  96 */
  97int parse_cpu_list(char *cpu_list, char **monitored_cpus)
  98{
  99        char *mon_cpus;
 100        const char *p;
 101        int end_cpu;
 102        int nr_cpus;
 103        int cpu;
 104        int i;
 105
 106        nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
 107
 108        mon_cpus = malloc(nr_cpus * sizeof(char));
 109        memset(mon_cpus, 0, (nr_cpus * sizeof(char)));
 110
 111        for (p = cpu_list; *p; ) {
 112                cpu = atoi(p);
 113                if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
 114                        goto err;
 115
 116                while (isdigit(*p))
 117                        p++;
 118                if (*p == '-') {
 119                        p++;
 120                        end_cpu = atoi(p);
 121                        if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
 122                                goto err;
 123                        while (isdigit(*p))
 124                                p++;
 125                } else
 126                        end_cpu = cpu;
 127
 128                if (cpu == end_cpu) {
 129                        debug_msg("cpu_list: adding cpu %d\n", cpu);
 130                        mon_cpus[cpu] = 1;
 131                } else {
 132                        for (i = cpu; i <= end_cpu; i++) {
 133                                debug_msg("cpu_list: adding cpu %d\n", i);
 134                                mon_cpus[i] = 1;
 135                        }
 136                }
 137
 138                if (*p == ',')
 139                        p++;
 140        }
 141
 142        *monitored_cpus = mon_cpus;
 143
 144        return 0;
 145
 146err:
 147        debug_msg("Error parsing the cpu list %s", cpu_list);
 148        return 1;
 149}
 150
 151/*
 152 * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
 153 */
 154long parse_seconds_duration(char *val)
 155{
 156        char *end;
 157        long t;
 158
 159        t = strtol(val, &end, 10);
 160
 161        if (end) {
 162                switch (*end) {
 163                case 's':
 164                case 'S':
 165                        break;
 166                case 'm':
 167                case 'M':
 168                        t *= 60;
 169                        break;
 170                case 'h':
 171                case 'H':
 172                        t *= 60 * 60;
 173                        break;
 174
 175                case 'd':
 176                case 'D':
 177                        t *= 24 * 60 * 60;
 178                        break;
 179                }
 180        }
 181
 182        return t;
 183}
 184
 185/*
 186 * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
 187 */
 188long parse_ns_duration(char *val)
 189{
 190        char *end;
 191        long t;
 192
 193        t = strtol(val, &end, 10);
 194
 195        if (end) {
 196                if (!strncmp(end, "ns", 2)) {
 197                        return t;
 198                } else if (!strncmp(end, "us", 2)) {
 199                        t *= 1000;
 200                        return t;
 201                } else if (!strncmp(end, "ms", 2)) {
 202                        t *= 1000 * 1000;
 203                        return t;
 204                } else if (!strncmp(end, "s", 1)) {
 205                        t *= 1000 * 1000 * 1000;
 206                        return t;
 207                }
 208                return -1;
 209        }
 210
 211        return t;
 212}
 213
 214/*
 215 * This is a set of helper functions to use SCHED_DEADLINE.
 216 */
 217#ifdef __x86_64__
 218# define __NR_sched_setattr     314
 219# define __NR_sched_getattr     315
 220#elif __i386__
 221# define __NR_sched_setattr     351
 222# define __NR_sched_getattr     352
 223#elif __arm__
 224# define __NR_sched_setattr     380
 225# define __NR_sched_getattr     381
 226#elif __aarch64__
 227# define __NR_sched_setattr     274
 228# define __NR_sched_getattr     275
 229#elif __powerpc__
 230# define __NR_sched_setattr     355
 231# define __NR_sched_getattr     356
 232#elif __s390x__
 233# define __NR_sched_setattr     345
 234# define __NR_sched_getattr     346
 235#endif
 236
 237#define SCHED_DEADLINE          6
 238
 239static inline int sched_setattr(pid_t pid, const struct sched_attr *attr,
 240                                unsigned int flags) {
 241        return syscall(__NR_sched_setattr, pid, attr, flags);
 242}
 243
 244static inline int sched_getattr(pid_t pid, struct sched_attr *attr,
 245                                unsigned int size, unsigned int flags)
 246{
 247        return syscall(__NR_sched_getattr, pid, attr, size, flags);
 248}
 249
 250int __set_sched_attr(int pid, struct sched_attr *attr)
 251{
 252        int flags = 0;
 253        int retval;
 254
 255        retval = sched_setattr(pid, attr, flags);
 256        if (retval < 0) {
 257                err_msg("boost_with_deadline failed to boost pid %d: %s\n",
 258                        pid, strerror(errno));
 259                return 1;
 260        }
 261
 262        return 0;
 263}
 264/*
 265 * set_comm_sched_attr - set sched params to threads starting with char *comm
 266 *
 267 * This function uses procps to list the currently running threads and then
 268 * set the sched_attr *attr to the threads that start with char *comm. It is
 269 * mainly used to set the priority to the kernel threads created by the
 270 * tracers.
 271 */
 272int set_comm_sched_attr(const char *comm, struct sched_attr *attr)
 273{
 274        int flags = PROC_FILLCOM | PROC_FILLSTAT;
 275        PROCTAB *ptp;
 276        proc_t task;
 277        int retval;
 278
 279        ptp = openproc(flags);
 280        if (!ptp) {
 281                err_msg("error openproc()\n");
 282                return -ENOENT;
 283        }
 284
 285        memset(&task, 0, sizeof(task));
 286
 287        while (readproc(ptp, &task)) {
 288                retval = strncmp(comm, task.cmd, strlen(comm));
 289                if (retval)
 290                        continue;
 291                retval = __set_sched_attr(task.tid, attr);
 292                if (retval)
 293                        goto out_err;
 294        }
 295
 296        closeproc(ptp);
 297        return 0;
 298
 299out_err:
 300        closeproc(ptp);
 301        return 1;
 302}
 303
 304#define INVALID_VAL     (~0L)
 305static long get_long_ns_after_colon(char *start)
 306{
 307        long val = INVALID_VAL;
 308
 309        /* find the ":" */
 310        start = strstr(start, ":");
 311        if (!start)
 312                return -1;
 313
 314        /* skip ":" */
 315        start++;
 316        val = parse_ns_duration(start);
 317
 318        return val;
 319}
 320
 321static long get_long_after_colon(char *start)
 322{
 323        long val = INVALID_VAL;
 324
 325        /* find the ":" */
 326        start = strstr(start, ":");
 327        if (!start)
 328                return -1;
 329
 330        /* skip ":" */
 331        start++;
 332        val = get_llong_from_str(start);
 333
 334        return val;
 335}
 336
 337/*
 338 * parse priority in the format:
 339 * SCHED_OTHER:
 340 *              o:<prio>
 341 *              O:<prio>
 342 * SCHED_RR:
 343 *              r:<prio>
 344 *              R:<prio>
 345 * SCHED_FIFO:
 346 *              f:<prio>
 347 *              F:<prio>
 348 * SCHED_DEADLINE:
 349 *              d:runtime:period
 350 *              D:runtime:period
 351 */
 352int parse_prio(char *arg, struct sched_attr *sched_param)
 353{
 354        long prio;
 355        long runtime;
 356        long period;
 357
 358        memset(sched_param, 0, sizeof(*sched_param));
 359        sched_param->size = sizeof(*sched_param);
 360
 361        switch (arg[0]) {
 362        case 'd':
 363        case 'D':
 364                /* d:runtime:period */
 365                if (strlen(arg) < 4)
 366                        return -1;
 367
 368                runtime = get_long_ns_after_colon(arg);
 369                if (runtime == INVALID_VAL)
 370                        return -1;
 371
 372                period = get_long_ns_after_colon(&arg[2]);
 373                if (period == INVALID_VAL)
 374                        return -1;
 375
 376                if (runtime > period)
 377                        return -1;
 378
 379                sched_param->sched_policy   = SCHED_DEADLINE;
 380                sched_param->sched_runtime  = runtime;
 381                sched_param->sched_deadline = period;
 382                sched_param->sched_period   = period;
 383                break;
 384        case 'f':
 385        case 'F':
 386                /* f:prio */
 387                prio = get_long_after_colon(arg);
 388                if (prio == INVALID_VAL)
 389                        return -1;
 390
 391                if (prio < sched_get_priority_min(SCHED_FIFO))
 392                        return -1;
 393                if (prio > sched_get_priority_max(SCHED_FIFO))
 394                        return -1;
 395
 396                sched_param->sched_policy   = SCHED_FIFO;
 397                sched_param->sched_priority = prio;
 398                break;
 399        case 'r':
 400        case 'R':
 401                /* r:prio */
 402                prio = get_long_after_colon(arg);
 403                if (prio == INVALID_VAL)
 404                        return -1;
 405
 406                if (prio < sched_get_priority_min(SCHED_RR))
 407                        return -1;
 408                if (prio > sched_get_priority_max(SCHED_RR))
 409                        return -1;
 410
 411                sched_param->sched_policy   = SCHED_RR;
 412                sched_param->sched_priority = prio;
 413                break;
 414        case 'o':
 415        case 'O':
 416                /* o:prio */
 417                prio = get_long_after_colon(arg);
 418                if (prio == INVALID_VAL)
 419                        return -1;
 420
 421                if (prio < sched_get_priority_min(SCHED_OTHER))
 422                        return -1;
 423                if (prio > sched_get_priority_max(SCHED_OTHER))
 424                        return -1;
 425
 426                sched_param->sched_policy   = SCHED_OTHER;
 427                sched_param->sched_priority = prio;
 428                break;
 429        default:
 430                return -1;
 431        }
 432        return 0;
 433}
 434