root/tools/perf/util/time-utils.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_nsec_time
  2. parse_timestr_sec_nsec
  3. split_start_end
  4. perf_time__parse_str
  5. perf_time__parse_strs
  6. parse_percent
  7. set_percent_time
  8. percent_slash_split
  9. percent_dash_split
  10. percent_comma_split
  11. one_percent_convert
  12. perf_time__percent_parse_str
  13. perf_time__range_alloc
  14. perf_time__skip_sample
  15. perf_time__ranges_skip_sample
  16. perf_time__parse_for_ranges_reltime
  17. perf_time__parse_for_ranges
  18. timestamp__scnprintf_usec
  19. timestamp__scnprintf_nsec
  20. fetch_current_timestamp

   1 // SPDX-License-Identifier: GPL-2.0
   2 #include <stdlib.h>
   3 #include <string.h>
   4 #include <linux/string.h>
   5 #include <sys/time.h>
   6 #include <linux/time64.h>
   7 #include <time.h>
   8 #include <errno.h>
   9 #include <inttypes.h>
  10 #include <math.h>
  11 #include <linux/ctype.h>
  12 
  13 #include "debug.h"
  14 #include "time-utils.h"
  15 #include "session.h"
  16 #include "evlist.h"
  17 
  18 int parse_nsec_time(const char *str, u64 *ptime)
  19 {
  20         u64 time_sec, time_nsec;
  21         char *end;
  22 
  23         time_sec = strtoul(str, &end, 10);
  24         if (*end != '.' && *end != '\0')
  25                 return -1;
  26 
  27         if (*end == '.') {
  28                 int i;
  29                 char nsec_buf[10];
  30 
  31                 if (strlen(++end) > 9)
  32                         return -1;
  33 
  34                 strncpy(nsec_buf, end, 9);
  35                 nsec_buf[9] = '\0';
  36 
  37                 /* make it nsec precision */
  38                 for (i = strlen(nsec_buf); i < 9; i++)
  39                         nsec_buf[i] = '0';
  40 
  41                 time_nsec = strtoul(nsec_buf, &end, 10);
  42                 if (*end != '\0')
  43                         return -1;
  44         } else
  45                 time_nsec = 0;
  46 
  47         *ptime = time_sec * NSEC_PER_SEC + time_nsec;
  48         return 0;
  49 }
  50 
  51 static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,
  52                                   char *start_str, char *end_str)
  53 {
  54         if (start_str && (*start_str != '\0') &&
  55             (parse_nsec_time(start_str, &ptime->start) != 0)) {
  56                 return -1;
  57         }
  58 
  59         if (end_str && (*end_str != '\0') &&
  60             (parse_nsec_time(end_str, &ptime->end) != 0)) {
  61                 return -1;
  62         }
  63 
  64         return 0;
  65 }
  66 
  67 static int split_start_end(char **start, char **end, const char *ostr, char ch)
  68 {
  69         char *start_str, *end_str;
  70         char *d, *str;
  71 
  72         if (ostr == NULL || *ostr == '\0')
  73                 return 0;
  74 
  75         /* copy original string because we need to modify it */
  76         str = strdup(ostr);
  77         if (str == NULL)
  78                 return -ENOMEM;
  79 
  80         start_str = str;
  81         d = strchr(start_str, ch);
  82         if (d) {
  83                 *d = '\0';
  84                 ++d;
  85         }
  86         end_str = d;
  87 
  88         *start = start_str;
  89         *end = end_str;
  90 
  91         return 0;
  92 }
  93 
  94 int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
  95 {
  96         char *start_str = NULL, *end_str;
  97         int rc;
  98 
  99         rc = split_start_end(&start_str, &end_str, ostr, ',');
 100         if (rc || !start_str)
 101                 return rc;
 102 
 103         ptime->start = 0;
 104         ptime->end = 0;
 105 
 106         rc = parse_timestr_sec_nsec(ptime, start_str, end_str);
 107 
 108         free(start_str);
 109 
 110         /* make sure end time is after start time if it was given */
 111         if (rc == 0 && ptime->end && ptime->end < ptime->start)
 112                 return -EINVAL;
 113 
 114         pr_debug("start time %" PRIu64 ", ", ptime->start);
 115         pr_debug("end time %" PRIu64 "\n", ptime->end);
 116 
 117         return rc;
 118 }
 119 
 120 static int perf_time__parse_strs(struct perf_time_interval *ptime,
 121                                  const char *ostr, int size)
 122 {
 123         const char *cp;
 124         char *str, *arg, *p;
 125         int i, num = 0, rc = 0;
 126 
 127         /* Count the commas */
 128         for (cp = ostr; *cp; cp++)
 129                 num += !!(*cp == ',');
 130 
 131         if (!num)
 132                 return -EINVAL;
 133 
 134         BUG_ON(num > size);
 135 
 136         str = strdup(ostr);
 137         if (!str)
 138                 return -ENOMEM;
 139 
 140         /* Split the string and parse each piece, except the last */
 141         for (i = 0, p = str; i < num - 1; i++) {
 142                 arg = p;
 143                 /* Find next comma, there must be one */
 144                 p = skip_spaces(strchr(p, ',') + 1);
 145                 /* Skip the value, must not contain space or comma */
 146                 while (*p && !isspace(*p)) {
 147                         if (*p++ == ',') {
 148                                 rc = -EINVAL;
 149                                 goto out;
 150                         }
 151                 }
 152                 /* Split and parse */
 153                 if (*p)
 154                         *p++ = 0;
 155                 rc = perf_time__parse_str(ptime + i, arg);
 156                 if (rc < 0)
 157                         goto out;
 158         }
 159 
 160         /* Parse the last piece */
 161         rc = perf_time__parse_str(ptime + i, p);
 162         if (rc < 0)
 163                 goto out;
 164 
 165         /* Check there is no overlap */
 166         for (i = 0; i < num - 1; i++) {
 167                 if (ptime[i].end >= ptime[i + 1].start) {
 168                         rc = -EINVAL;
 169                         goto out;
 170                 }
 171         }
 172 
 173         rc = num;
 174 out:
 175         free(str);
 176 
 177         return rc;
 178 }
 179 
 180 static int parse_percent(double *pcnt, char *str)
 181 {
 182         char *c, *endptr;
 183         double d;
 184 
 185         c = strchr(str, '%');
 186         if (c)
 187                 *c = '\0';
 188         else
 189                 return -1;
 190 
 191         d = strtod(str, &endptr);
 192         if (endptr != str + strlen(str))
 193                 return -1;
 194 
 195         *pcnt = d / 100.0;
 196         return 0;
 197 }
 198 
 199 static int set_percent_time(struct perf_time_interval *ptime, double start_pcnt,
 200                             double end_pcnt, u64 start, u64 end)
 201 {
 202         u64 total = end - start;
 203 
 204         if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
 205             end_pcnt < 0.0 || end_pcnt > 1.0) {
 206                 return -1;
 207         }
 208 
 209         ptime->start = start + round(start_pcnt * total);
 210         ptime->end = start + round(end_pcnt * total);
 211 
 212         if (ptime->end > ptime->start && ptime->end != end)
 213                 ptime->end -= 1;
 214 
 215         return 0;
 216 }
 217 
 218 static int percent_slash_split(char *str, struct perf_time_interval *ptime,
 219                                u64 start, u64 end)
 220 {
 221         char *p, *end_str;
 222         double pcnt, start_pcnt, end_pcnt;
 223         int i;
 224 
 225         /*
 226          * Example:
 227          * 10%/2: select the second 10% slice and the third 10% slice
 228          */
 229 
 230         /* We can modify this string since the original one is copied */
 231         p = strchr(str, '/');
 232         if (!p)
 233                 return -1;
 234 
 235         *p = '\0';
 236         if (parse_percent(&pcnt, str) < 0)
 237                 return -1;
 238 
 239         p++;
 240         i = (int)strtol(p, &end_str, 10);
 241         if (*end_str)
 242                 return -1;
 243 
 244         if (pcnt <= 0.0)
 245                 return -1;
 246 
 247         start_pcnt = pcnt * (i - 1);
 248         end_pcnt = pcnt * i;
 249 
 250         return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);
 251 }
 252 
 253 static int percent_dash_split(char *str, struct perf_time_interval *ptime,
 254                               u64 start, u64 end)
 255 {
 256         char *start_str = NULL, *end_str;
 257         double start_pcnt, end_pcnt;
 258         int ret;
 259 
 260         /*
 261          * Example: 0%-10%
 262          */
 263 
 264         ret = split_start_end(&start_str, &end_str, str, '-');
 265         if (ret || !start_str)
 266                 return ret;
 267 
 268         if ((parse_percent(&start_pcnt, start_str) != 0) ||
 269             (parse_percent(&end_pcnt, end_str) != 0)) {
 270                 free(start_str);
 271                 return -1;
 272         }
 273 
 274         free(start_str);
 275 
 276         return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);
 277 }
 278 
 279 typedef int (*time_pecent_split)(char *, struct perf_time_interval *,
 280                                  u64 start, u64 end);
 281 
 282 static int percent_comma_split(struct perf_time_interval *ptime_buf, int num,
 283                                const char *ostr, u64 start, u64 end,
 284                                time_pecent_split func)
 285 {
 286         char *str, *p1, *p2;
 287         int len, ret, i = 0;
 288 
 289         str = strdup(ostr);
 290         if (str == NULL)
 291                 return -ENOMEM;
 292 
 293         len = strlen(str);
 294         p1 = str;
 295 
 296         while (p1 < str + len) {
 297                 if (i >= num) {
 298                         free(str);
 299                         return -1;
 300                 }
 301 
 302                 p2 = strchr(p1, ',');
 303                 if (p2)
 304                         *p2 = '\0';
 305 
 306                 ret = (func)(p1, &ptime_buf[i], start, end);
 307                 if (ret < 0) {
 308                         free(str);
 309                         return -1;
 310                 }
 311 
 312                 pr_debug("start time %d: %" PRIu64 ", ", i, ptime_buf[i].start);
 313                 pr_debug("end time %d: %" PRIu64 "\n", i, ptime_buf[i].end);
 314 
 315                 i++;
 316 
 317                 if (p2)
 318                         p1 = p2 + 1;
 319                 else
 320                         break;
 321         }
 322 
 323         free(str);
 324         return i;
 325 }
 326 
 327 static int one_percent_convert(struct perf_time_interval *ptime_buf,
 328                                const char *ostr, u64 start, u64 end, char *c)
 329 {
 330         char *str;
 331         int len = strlen(ostr), ret;
 332 
 333         /*
 334          * c points to '%'.
 335          * '%' should be the last character
 336          */
 337         if (ostr + len - 1 != c)
 338                 return -1;
 339 
 340         /*
 341          * Construct a string like "xx%/1"
 342          */
 343         str = malloc(len + 3);
 344         if (str == NULL)
 345                 return -ENOMEM;
 346 
 347         memcpy(str, ostr, len);
 348         strcpy(str + len, "/1");
 349 
 350         ret = percent_slash_split(str, ptime_buf, start, end);
 351         if (ret == 0)
 352                 ret = 1;
 353 
 354         free(str);
 355         return ret;
 356 }
 357 
 358 int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,
 359                                  const char *ostr, u64 start, u64 end)
 360 {
 361         char *c;
 362 
 363         /*
 364          * ostr example:
 365          * 10%/2,10%/3: select the second 10% slice and the third 10% slice
 366          * 0%-10%,30%-40%: multiple time range
 367          * 50%: just one percent
 368          */
 369 
 370         memset(ptime_buf, 0, sizeof(*ptime_buf) * num);
 371 
 372         c = strchr(ostr, '/');
 373         if (c) {
 374                 return percent_comma_split(ptime_buf, num, ostr, start,
 375                                            end, percent_slash_split);
 376         }
 377 
 378         c = strchr(ostr, '-');
 379         if (c) {
 380                 return percent_comma_split(ptime_buf, num, ostr, start,
 381                                            end, percent_dash_split);
 382         }
 383 
 384         c = strchr(ostr, '%');
 385         if (c)
 386                 return one_percent_convert(ptime_buf, ostr, start, end, c);
 387 
 388         return -1;
 389 }
 390 
 391 struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size)
 392 {
 393         const char *p1, *p2;
 394         int i = 1;
 395         struct perf_time_interval *ptime;
 396 
 397         /*
 398          * At least allocate one time range.
 399          */
 400         if (!ostr)
 401                 goto alloc;
 402 
 403         p1 = ostr;
 404         while (p1 < ostr + strlen(ostr)) {
 405                 p2 = strchr(p1, ',');
 406                 if (!p2)
 407                         break;
 408 
 409                 p1 = p2 + 1;
 410                 i++;
 411         }
 412 
 413 alloc:
 414         *size = i;
 415         ptime = calloc(i, sizeof(*ptime));
 416         return ptime;
 417 }
 418 
 419 bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
 420 {
 421         /* if time is not set don't drop sample */
 422         if (timestamp == 0)
 423                 return false;
 424 
 425         /* otherwise compare sample time to time window */
 426         if ((ptime->start && timestamp < ptime->start) ||
 427             (ptime->end && timestamp > ptime->end)) {
 428                 return true;
 429         }
 430 
 431         return false;
 432 }
 433 
 434 bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
 435                                    int num, u64 timestamp)
 436 {
 437         struct perf_time_interval *ptime;
 438         int i;
 439 
 440         if ((!ptime_buf) || (timestamp == 0) || (num == 0))
 441                 return false;
 442 
 443         if (num == 1)
 444                 return perf_time__skip_sample(&ptime_buf[0], timestamp);
 445 
 446         /*
 447          * start/end of multiple time ranges must be valid.
 448          */
 449         for (i = 0; i < num; i++) {
 450                 ptime = &ptime_buf[i];
 451 
 452                 if (timestamp >= ptime->start &&
 453                     (timestamp <= ptime->end || !ptime->end)) {
 454                         return false;
 455                 }
 456         }
 457 
 458         return true;
 459 }
 460 
 461 int perf_time__parse_for_ranges_reltime(const char *time_str,
 462                                 struct perf_session *session,
 463                                 struct perf_time_interval **ranges,
 464                                 int *range_size, int *range_num,
 465                                 bool reltime)
 466 {
 467         bool has_percent = strchr(time_str, '%');
 468         struct perf_time_interval *ptime_range;
 469         int size, num, ret = -EINVAL;
 470 
 471         ptime_range = perf_time__range_alloc(time_str, &size);
 472         if (!ptime_range)
 473                 return -ENOMEM;
 474 
 475         if (has_percent || reltime) {
 476                 if (session->evlist->first_sample_time == 0 &&
 477                     session->evlist->last_sample_time == 0) {
 478                         pr_err("HINT: no first/last sample time found in perf data.\n"
 479                                "Please use latest perf binary to execute 'perf record'\n"
 480                                "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
 481                         goto error;
 482                 }
 483         }
 484 
 485         if (has_percent) {
 486                 num = perf_time__percent_parse_str(
 487                                 ptime_range, size,
 488                                 time_str,
 489                                 session->evlist->first_sample_time,
 490                                 session->evlist->last_sample_time);
 491         } else {
 492                 num = perf_time__parse_strs(ptime_range, time_str, size);
 493         }
 494 
 495         if (num < 0)
 496                 goto error_invalid;
 497 
 498         if (reltime) {
 499                 int i;
 500 
 501                 for (i = 0; i < num; i++) {
 502                         ptime_range[i].start += session->evlist->first_sample_time;
 503                         ptime_range[i].end += session->evlist->first_sample_time;
 504                 }
 505         }
 506 
 507         *range_size = size;
 508         *range_num = num;
 509         *ranges = ptime_range;
 510         return 0;
 511 
 512 error_invalid:
 513         pr_err("Invalid time string\n");
 514 error:
 515         free(ptime_range);
 516         return ret;
 517 }
 518 
 519 int perf_time__parse_for_ranges(const char *time_str,
 520                                 struct perf_session *session,
 521                                 struct perf_time_interval **ranges,
 522                                 int *range_size, int *range_num)
 523 {
 524         return perf_time__parse_for_ranges_reltime(time_str, session, ranges,
 525                                         range_size, range_num, false);
 526 }
 527 
 528 int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
 529 {
 530         u64  sec = timestamp / NSEC_PER_SEC;
 531         u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC;
 532 
 533         return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec);
 534 }
 535 
 536 int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz)
 537 {
 538         u64 sec  = timestamp / NSEC_PER_SEC,
 539             nsec = timestamp % NSEC_PER_SEC;
 540 
 541         return scnprintf(buf, sz, "%" PRIu64 ".%09" PRIu64, sec, nsec);
 542 }
 543 
 544 int fetch_current_timestamp(char *buf, size_t sz)
 545 {
 546         struct timeval tv;
 547         struct tm tm;
 548         char dt[32];
 549 
 550         if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm))
 551                 return -1;
 552 
 553         if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm))
 554                 return -1;
 555 
 556         scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000);
 557 
 558         return 0;
 559 }

/* [<][>][^][v][top][bottom][index][help] */