linux/tools/perf/util/time-utils.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <stdlib.h>
   3#include <string.h>
   4#include <sys/time.h>
   5#include <linux/time64.h>
   6#include <time.h>
   7#include <errno.h>
   8#include <inttypes.h>
   9#include <math.h>
  10
  11#include "perf.h"
  12#include "debug.h"
  13#include "time-utils.h"
  14
  15int parse_nsec_time(const char *str, u64 *ptime)
  16{
  17        u64 time_sec, time_nsec;
  18        char *end;
  19
  20        time_sec = strtoul(str, &end, 10);
  21        if (*end != '.' && *end != '\0')
  22                return -1;
  23
  24        if (*end == '.') {
  25                int i;
  26                char nsec_buf[10];
  27
  28                if (strlen(++end) > 9)
  29                        return -1;
  30
  31                strncpy(nsec_buf, end, 9);
  32                nsec_buf[9] = '\0';
  33
  34                /* make it nsec precision */
  35                for (i = strlen(nsec_buf); i < 9; i++)
  36                        nsec_buf[i] = '0';
  37
  38                time_nsec = strtoul(nsec_buf, &end, 10);
  39                if (*end != '\0')
  40                        return -1;
  41        } else
  42                time_nsec = 0;
  43
  44        *ptime = time_sec * NSEC_PER_SEC + time_nsec;
  45        return 0;
  46}
  47
  48static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,
  49                                  char *start_str, char *end_str)
  50{
  51        if (start_str && (*start_str != '\0') &&
  52            (parse_nsec_time(start_str, &ptime->start) != 0)) {
  53                return -1;
  54        }
  55
  56        if (end_str && (*end_str != '\0') &&
  57            (parse_nsec_time(end_str, &ptime->end) != 0)) {
  58                return -1;
  59        }
  60
  61        return 0;
  62}
  63
  64static int split_start_end(char **start, char **end, const char *ostr, char ch)
  65{
  66        char *start_str, *end_str;
  67        char *d, *str;
  68
  69        if (ostr == NULL || *ostr == '\0')
  70                return 0;
  71
  72        /* copy original string because we need to modify it */
  73        str = strdup(ostr);
  74        if (str == NULL)
  75                return -ENOMEM;
  76
  77        start_str = str;
  78        d = strchr(start_str, ch);
  79        if (d) {
  80                *d = '\0';
  81                ++d;
  82        }
  83        end_str = d;
  84
  85        *start = start_str;
  86        *end = end_str;
  87
  88        return 0;
  89}
  90
  91int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
  92{
  93        char *start_str = NULL, *end_str;
  94        int rc;
  95
  96        rc = split_start_end(&start_str, &end_str, ostr, ',');
  97        if (rc || !start_str)
  98                return rc;
  99
 100        ptime->start = 0;
 101        ptime->end = 0;
 102
 103        rc = parse_timestr_sec_nsec(ptime, start_str, end_str);
 104
 105        free(start_str);
 106
 107        /* make sure end time is after start time if it was given */
 108        if (rc == 0 && ptime->end && ptime->end < ptime->start)
 109                return -EINVAL;
 110
 111        pr_debug("start time %" PRIu64 ", ", ptime->start);
 112        pr_debug("end time %" PRIu64 "\n", ptime->end);
 113
 114        return rc;
 115}
 116
 117static int parse_percent(double *pcnt, char *str)
 118{
 119        char *c, *endptr;
 120        double d;
 121
 122        c = strchr(str, '%');
 123        if (c)
 124                *c = '\0';
 125        else
 126                return -1;
 127
 128        d = strtod(str, &endptr);
 129        if (endptr != str + strlen(str))
 130                return -1;
 131
 132        *pcnt = d / 100.0;
 133        return 0;
 134}
 135
 136static int percent_slash_split(char *str, struct perf_time_interval *ptime,
 137                               u64 start, u64 end)
 138{
 139        char *p, *end_str;
 140        double pcnt, start_pcnt, end_pcnt;
 141        u64 total = end - start;
 142        int i;
 143
 144        /*
 145         * Example:
 146         * 10%/2: select the second 10% slice and the third 10% slice
 147         */
 148
 149        /* We can modify this string since the original one is copied */
 150        p = strchr(str, '/');
 151        if (!p)
 152                return -1;
 153
 154        *p = '\0';
 155        if (parse_percent(&pcnt, str) < 0)
 156                return -1;
 157
 158        p++;
 159        i = (int)strtol(p, &end_str, 10);
 160        if (*end_str)
 161                return -1;
 162
 163        if (pcnt <= 0.0)
 164                return -1;
 165
 166        start_pcnt = pcnt * (i - 1);
 167        end_pcnt = pcnt * i;
 168
 169        if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
 170            end_pcnt < 0.0 || end_pcnt > 1.0) {
 171                return -1;
 172        }
 173
 174        ptime->start = start + round(start_pcnt * total);
 175        ptime->end = start + round(end_pcnt * total);
 176
 177        return 0;
 178}
 179
 180static int percent_dash_split(char *str, struct perf_time_interval *ptime,
 181                              u64 start, u64 end)
 182{
 183        char *start_str = NULL, *end_str;
 184        double start_pcnt, end_pcnt;
 185        u64 total = end - start;
 186        int ret;
 187
 188        /*
 189         * Example: 0%-10%
 190         */
 191
 192        ret = split_start_end(&start_str, &end_str, str, '-');
 193        if (ret || !start_str)
 194                return ret;
 195
 196        if ((parse_percent(&start_pcnt, start_str) != 0) ||
 197            (parse_percent(&end_pcnt, end_str) != 0)) {
 198                free(start_str);
 199                return -1;
 200        }
 201
 202        free(start_str);
 203
 204        if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
 205            end_pcnt < 0.0 || end_pcnt > 1.0 ||
 206            start_pcnt > end_pcnt) {
 207                return -1;
 208        }
 209
 210        ptime->start = start + round(start_pcnt * total);
 211        ptime->end = start + round(end_pcnt * total);
 212
 213        return 0;
 214}
 215
 216typedef int (*time_pecent_split)(char *, struct perf_time_interval *,
 217                                 u64 start, u64 end);
 218
 219static int percent_comma_split(struct perf_time_interval *ptime_buf, int num,
 220                               const char *ostr, u64 start, u64 end,
 221                               time_pecent_split func)
 222{
 223        char *str, *p1, *p2;
 224        int len, ret, i = 0;
 225
 226        str = strdup(ostr);
 227        if (str == NULL)
 228                return -ENOMEM;
 229
 230        len = strlen(str);
 231        p1 = str;
 232
 233        while (p1 < str + len) {
 234                if (i >= num) {
 235                        free(str);
 236                        return -1;
 237                }
 238
 239                p2 = strchr(p1, ',');
 240                if (p2)
 241                        *p2 = '\0';
 242
 243                ret = (func)(p1, &ptime_buf[i], start, end);
 244                if (ret < 0) {
 245                        free(str);
 246                        return -1;
 247                }
 248
 249                pr_debug("start time %d: %" PRIu64 ", ", i, ptime_buf[i].start);
 250                pr_debug("end time %d: %" PRIu64 "\n", i, ptime_buf[i].end);
 251
 252                i++;
 253
 254                if (p2)
 255                        p1 = p2 + 1;
 256                else
 257                        break;
 258        }
 259
 260        free(str);
 261        return i;
 262}
 263
 264static int one_percent_convert(struct perf_time_interval *ptime_buf,
 265                               const char *ostr, u64 start, u64 end, char *c)
 266{
 267        char *str;
 268        int len = strlen(ostr), ret;
 269
 270        /*
 271         * c points to '%'.
 272         * '%' should be the last character
 273         */
 274        if (ostr + len - 1 != c)
 275                return -1;
 276
 277        /*
 278         * Construct a string like "xx%/1"
 279         */
 280        str = malloc(len + 3);
 281        if (str == NULL)
 282                return -ENOMEM;
 283
 284        memcpy(str, ostr, len);
 285        strcpy(str + len, "/1");
 286
 287        ret = percent_slash_split(str, ptime_buf, start, end);
 288        if (ret == 0)
 289                ret = 1;
 290
 291        free(str);
 292        return ret;
 293}
 294
 295int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,
 296                                 const char *ostr, u64 start, u64 end)
 297{
 298        char *c;
 299
 300        /*
 301         * ostr example:
 302         * 10%/2,10%/3: select the second 10% slice and the third 10% slice
 303         * 0%-10%,30%-40%: multiple time range
 304         * 50%: just one percent
 305         */
 306
 307        memset(ptime_buf, 0, sizeof(*ptime_buf) * num);
 308
 309        c = strchr(ostr, '/');
 310        if (c) {
 311                return percent_comma_split(ptime_buf, num, ostr, start,
 312                                           end, percent_slash_split);
 313        }
 314
 315        c = strchr(ostr, '-');
 316        if (c) {
 317                return percent_comma_split(ptime_buf, num, ostr, start,
 318                                           end, percent_dash_split);
 319        }
 320
 321        c = strchr(ostr, '%');
 322        if (c)
 323                return one_percent_convert(ptime_buf, ostr, start, end, c);
 324
 325        return -1;
 326}
 327
 328struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size)
 329{
 330        const char *p1, *p2;
 331        int i = 1;
 332        struct perf_time_interval *ptime;
 333
 334        /*
 335         * At least allocate one time range.
 336         */
 337        if (!ostr)
 338                goto alloc;
 339
 340        p1 = ostr;
 341        while (p1 < ostr + strlen(ostr)) {
 342                p2 = strchr(p1, ',');
 343                if (!p2)
 344                        break;
 345
 346                p1 = p2 + 1;
 347                i++;
 348        }
 349
 350alloc:
 351        *size = i;
 352        ptime = calloc(i, sizeof(*ptime));
 353        return ptime;
 354}
 355
 356bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
 357{
 358        /* if time is not set don't drop sample */
 359        if (timestamp == 0)
 360                return false;
 361
 362        /* otherwise compare sample time to time window */
 363        if ((ptime->start && timestamp < ptime->start) ||
 364            (ptime->end && timestamp > ptime->end)) {
 365                return true;
 366        }
 367
 368        return false;
 369}
 370
 371bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
 372                                   int num, u64 timestamp)
 373{
 374        struct perf_time_interval *ptime;
 375        int i;
 376
 377        if ((timestamp == 0) || (num == 0))
 378                return false;
 379
 380        if (num == 1)
 381                return perf_time__skip_sample(&ptime_buf[0], timestamp);
 382
 383        /*
 384         * start/end of multiple time ranges must be valid.
 385         */
 386        for (i = 0; i < num; i++) {
 387                ptime = &ptime_buf[i];
 388
 389                if (timestamp >= ptime->start &&
 390                    ((timestamp < ptime->end && i < num - 1) ||
 391                     (timestamp <= ptime->end && i == num - 1))) {
 392                        break;
 393                }
 394        }
 395
 396        return (i == num) ? true : false;
 397}
 398
 399int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
 400{
 401        u64  sec = timestamp / NSEC_PER_SEC;
 402        u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC;
 403
 404        return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec);
 405}
 406
 407int fetch_current_timestamp(char *buf, size_t sz)
 408{
 409        struct timeval tv;
 410        struct tm tm;
 411        char dt[32];
 412
 413        if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm))
 414                return -1;
 415
 416        if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm))
 417                return -1;
 418
 419        scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000);
 420
 421        return 0;
 422}
 423