| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | 2 | #include <stdlib.h> |
|---|
| 3 | 3 | #include <string.h> |
|---|
| 4 | +#include <linux/string.h> |
|---|
| 4 | 5 | #include <sys/time.h> |
|---|
| 5 | 6 | #include <linux/time64.h> |
|---|
| 6 | 7 | #include <time.h> |
|---|
| 7 | 8 | #include <errno.h> |
|---|
| 8 | 9 | #include <inttypes.h> |
|---|
| 9 | 10 | #include <math.h> |
|---|
| 11 | +#include <linux/ctype.h> |
|---|
| 10 | 12 | |
|---|
| 11 | | -#include "perf.h" |
|---|
| 12 | 13 | #include "debug.h" |
|---|
| 13 | 14 | #include "time-utils.h" |
|---|
| 15 | +#include "session.h" |
|---|
| 16 | +#include "evlist.h" |
|---|
| 14 | 17 | |
|---|
| 15 | 18 | int parse_nsec_time(const char *str, u64 *ptime) |
|---|
| 16 | 19 | { |
|---|
| .. | .. |
|---|
| 114 | 117 | return rc; |
|---|
| 115 | 118 | } |
|---|
| 116 | 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 | + |
|---|
| 117 | 180 | static int parse_percent(double *pcnt, char *str) |
|---|
| 118 | 181 | { |
|---|
| 119 | 182 | char *c, *endptr; |
|---|
| .. | .. |
|---|
| 133 | 196 | return 0; |
|---|
| 134 | 197 | } |
|---|
| 135 | 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 | + |
|---|
| 136 | 218 | static int percent_slash_split(char *str, struct perf_time_interval *ptime, |
|---|
| 137 | 219 | u64 start, u64 end) |
|---|
| 138 | 220 | { |
|---|
| 139 | 221 | char *p, *end_str; |
|---|
| 140 | 222 | double pcnt, start_pcnt, end_pcnt; |
|---|
| 141 | | - u64 total = end - start; |
|---|
| 142 | 223 | int i; |
|---|
| 143 | 224 | |
|---|
| 144 | 225 | /* |
|---|
| .. | .. |
|---|
| 166 | 247 | start_pcnt = pcnt * (i - 1); |
|---|
| 167 | 248 | end_pcnt = pcnt * i; |
|---|
| 168 | 249 | |
|---|
| 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; |
|---|
| 250 | + return set_percent_time(ptime, start_pcnt, end_pcnt, start, end); |
|---|
| 178 | 251 | } |
|---|
| 179 | 252 | |
|---|
| 180 | 253 | static int percent_dash_split(char *str, struct perf_time_interval *ptime, |
|---|
| .. | .. |
|---|
| 182 | 255 | { |
|---|
| 183 | 256 | char *start_str = NULL, *end_str; |
|---|
| 184 | 257 | double start_pcnt, end_pcnt; |
|---|
| 185 | | - u64 total = end - start; |
|---|
| 186 | 258 | int ret; |
|---|
| 187 | 259 | |
|---|
| 188 | 260 | /* |
|---|
| .. | .. |
|---|
| 201 | 273 | |
|---|
| 202 | 274 | free(start_str); |
|---|
| 203 | 275 | |
|---|
| 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; |
|---|
| 276 | + return set_percent_time(ptime, start_pcnt, end_pcnt, start, end); |
|---|
| 214 | 277 | } |
|---|
| 215 | 278 | |
|---|
| 216 | 279 | typedef int (*time_pecent_split)(char *, struct perf_time_interval *, |
|---|
| .. | .. |
|---|
| 374 | 437 | struct perf_time_interval *ptime; |
|---|
| 375 | 438 | int i; |
|---|
| 376 | 439 | |
|---|
| 377 | | - if ((timestamp == 0) || (num == 0)) |
|---|
| 440 | + if ((!ptime_buf) || (timestamp == 0) || (num == 0)) |
|---|
| 378 | 441 | return false; |
|---|
| 379 | 442 | |
|---|
| 380 | 443 | if (num == 1) |
|---|
| .. | .. |
|---|
| 387 | 450 | ptime = &ptime_buf[i]; |
|---|
| 388 | 451 | |
|---|
| 389 | 452 | if (timestamp >= ptime->start && |
|---|
| 390 | | - ((timestamp < ptime->end && i < num - 1) || |
|---|
| 391 | | - (timestamp <= ptime->end && i == num - 1))) { |
|---|
| 392 | | - break; |
|---|
| 453 | + (timestamp <= ptime->end || !ptime->end)) { |
|---|
| 454 | + return false; |
|---|
| 393 | 455 | } |
|---|
| 394 | 456 | } |
|---|
| 395 | 457 | |
|---|
| 396 | | - return (i == num) ? true : false; |
|---|
| 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); |
|---|
| 397 | 526 | } |
|---|
| 398 | 527 | |
|---|
| 399 | 528 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) |
|---|
| .. | .. |
|---|
| 404 | 533 | return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); |
|---|
| 405 | 534 | } |
|---|
| 406 | 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 | + |
|---|
| 407 | 544 | int fetch_current_timestamp(char *buf, size_t sz) |
|---|
| 408 | 545 | { |
|---|
| 409 | 546 | struct timeval tv; |
|---|