.. | .. |
---|
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; |
---|