]>
git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blob - tools/perf/util/time-utils.c
1 // SPDX-License-Identifier: GPL-2.0
4 #include <linux/string.h>
6 #include <linux/time64.h>
11 #include <linux/ctype.h>
15 #include "time-utils.h"
19 int parse_nsec_time(const char *str
, u64
*ptime
)
21 u64 time_sec
, time_nsec
;
24 time_sec
= strtoul(str
, &end
, 10);
25 if (*end
!= '.' && *end
!= '\0')
32 if (strlen(++end
) > 9)
35 strncpy(nsec_buf
, end
, 9);
38 /* make it nsec precision */
39 for (i
= strlen(nsec_buf
); i
< 9; i
++)
42 time_nsec
= strtoul(nsec_buf
, &end
, 10);
48 *ptime
= time_sec
* NSEC_PER_SEC
+ time_nsec
;
52 static int parse_timestr_sec_nsec(struct perf_time_interval
*ptime
,
53 char *start_str
, char *end_str
)
55 if (start_str
&& (*start_str
!= '\0') &&
56 (parse_nsec_time(start_str
, &ptime
->start
) != 0)) {
60 if (end_str
&& (*end_str
!= '\0') &&
61 (parse_nsec_time(end_str
, &ptime
->end
) != 0)) {
68 static int split_start_end(char **start
, char **end
, const char *ostr
, char ch
)
70 char *start_str
, *end_str
;
73 if (ostr
== NULL
|| *ostr
== '\0')
76 /* copy original string because we need to modify it */
82 d
= strchr(start_str
, ch
);
95 int perf_time__parse_str(struct perf_time_interval
*ptime
, const char *ostr
)
97 char *start_str
= NULL
, *end_str
;
100 rc
= split_start_end(&start_str
, &end_str
, ostr
, ',');
101 if (rc
|| !start_str
)
107 rc
= parse_timestr_sec_nsec(ptime
, start_str
, end_str
);
111 /* make sure end time is after start time if it was given */
112 if (rc
== 0 && ptime
->end
&& ptime
->end
< ptime
->start
)
115 pr_debug("start time %" PRIu64
", ", ptime
->start
);
116 pr_debug("end time %" PRIu64
"\n", ptime
->end
);
121 static int perf_time__parse_strs(struct perf_time_interval
*ptime
,
122 const char *ostr
, int size
)
126 int i
, num
= 0, rc
= 0;
128 /* Count the commas */
129 for (cp
= ostr
; *cp
; cp
++)
130 num
+= !!(*cp
== ',');
141 /* Split the string and parse each piece, except the last */
142 for (i
= 0, p
= str
; i
< num
- 1; i
++) {
144 /* Find next comma, there must be one */
145 p
= skip_spaces(strchr(p
, ',') + 1);
146 /* Skip the value, must not contain space or comma */
147 while (*p
&& !isspace(*p
)) {
153 /* Split and parse */
156 rc
= perf_time__parse_str(ptime
+ i
, arg
);
161 /* Parse the last piece */
162 rc
= perf_time__parse_str(ptime
+ i
, p
);
166 /* Check there is no overlap */
167 for (i
= 0; i
< num
- 1; i
++) {
168 if (ptime
[i
].end
>= ptime
[i
+ 1].start
) {
181 static int parse_percent(double *pcnt
, char *str
)
186 c
= strchr(str
, '%');
192 d
= strtod(str
, &endptr
);
193 if (endptr
!= str
+ strlen(str
))
200 static int set_percent_time(struct perf_time_interval
*ptime
, double start_pcnt
,
201 double end_pcnt
, u64 start
, u64 end
)
203 u64 total
= end
- start
;
205 if (start_pcnt
< 0.0 || start_pcnt
> 1.0 ||
206 end_pcnt
< 0.0 || end_pcnt
> 1.0) {
210 ptime
->start
= start
+ round(start_pcnt
* total
);
211 ptime
->end
= start
+ round(end_pcnt
* total
);
213 if (ptime
->end
> ptime
->start
&& ptime
->end
!= end
)
219 static int percent_slash_split(char *str
, struct perf_time_interval
*ptime
,
223 double pcnt
, start_pcnt
, end_pcnt
;
228 * 10%/2: select the second 10% slice and the third 10% slice
231 /* We can modify this string since the original one is copied */
232 p
= strchr(str
, '/');
237 if (parse_percent(&pcnt
, str
) < 0)
241 i
= (int)strtol(p
, &end_str
, 10);
248 start_pcnt
= pcnt
* (i
- 1);
251 return set_percent_time(ptime
, start_pcnt
, end_pcnt
, start
, end
);
254 static int percent_dash_split(char *str
, struct perf_time_interval
*ptime
,
257 char *start_str
= NULL
, *end_str
;
258 double start_pcnt
, end_pcnt
;
265 ret
= split_start_end(&start_str
, &end_str
, str
, '-');
266 if (ret
|| !start_str
)
269 if ((parse_percent(&start_pcnt
, start_str
) != 0) ||
270 (parse_percent(&end_pcnt
, end_str
) != 0)) {
277 return set_percent_time(ptime
, start_pcnt
, end_pcnt
, start
, end
);
280 typedef int (*time_pecent_split
)(char *, struct perf_time_interval
*,
283 static int percent_comma_split(struct perf_time_interval
*ptime_buf
, int num
,
284 const char *ostr
, u64 start
, u64 end
,
285 time_pecent_split func
)
297 while (p1
< str
+ len
) {
303 p2
= strchr(p1
, ',');
307 ret
= (func
)(p1
, &ptime_buf
[i
], start
, end
);
313 pr_debug("start time %d: %" PRIu64
", ", i
, ptime_buf
[i
].start
);
314 pr_debug("end time %d: %" PRIu64
"\n", i
, ptime_buf
[i
].end
);
328 static int one_percent_convert(struct perf_time_interval
*ptime_buf
,
329 const char *ostr
, u64 start
, u64 end
, char *c
)
332 int len
= strlen(ostr
), ret
;
336 * '%' should be the last character
338 if (ostr
+ len
- 1 != c
)
342 * Construct a string like "xx%/1"
344 str
= malloc(len
+ 3);
348 memcpy(str
, ostr
, len
);
349 strcpy(str
+ len
, "/1");
351 ret
= percent_slash_split(str
, ptime_buf
, start
, end
);
359 int perf_time__percent_parse_str(struct perf_time_interval
*ptime_buf
, int num
,
360 const char *ostr
, u64 start
, u64 end
)
366 * 10%/2,10%/3: select the second 10% slice and the third 10% slice
367 * 0%-10%,30%-40%: multiple time range
368 * 50%: just one percent
371 memset(ptime_buf
, 0, sizeof(*ptime_buf
) * num
);
373 c
= strchr(ostr
, '/');
375 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
376 end
, percent_slash_split
);
379 c
= strchr(ostr
, '-');
381 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
382 end
, percent_dash_split
);
385 c
= strchr(ostr
, '%');
387 return one_percent_convert(ptime_buf
, ostr
, start
, end
, c
);
392 struct perf_time_interval
*perf_time__range_alloc(const char *ostr
, int *size
)
396 struct perf_time_interval
*ptime
;
399 * At least allocate one time range.
405 while (p1
< ostr
+ strlen(ostr
)) {
406 p2
= strchr(p1
, ',');
416 ptime
= calloc(i
, sizeof(*ptime
));
420 bool perf_time__skip_sample(struct perf_time_interval
*ptime
, u64 timestamp
)
422 /* if time is not set don't drop sample */
426 /* otherwise compare sample time to time window */
427 if ((ptime
->start
&& timestamp
< ptime
->start
) ||
428 (ptime
->end
&& timestamp
> ptime
->end
)) {
435 bool perf_time__ranges_skip_sample(struct perf_time_interval
*ptime_buf
,
436 int num
, u64 timestamp
)
438 struct perf_time_interval
*ptime
;
441 if ((!ptime_buf
) || (timestamp
== 0) || (num
== 0))
445 return perf_time__skip_sample(&ptime_buf
[0], timestamp
);
448 * start/end of multiple time ranges must be valid.
450 for (i
= 0; i
< num
; i
++) {
451 ptime
= &ptime_buf
[i
];
453 if (timestamp
>= ptime
->start
&&
454 (timestamp
<= ptime
->end
|| !ptime
->end
)) {
462 int perf_time__parse_for_ranges_reltime(const char *time_str
,
463 struct perf_session
*session
,
464 struct perf_time_interval
**ranges
,
465 int *range_size
, int *range_num
,
468 bool has_percent
= strchr(time_str
, '%');
469 struct perf_time_interval
*ptime_range
;
470 int size
, num
, ret
= -EINVAL
;
472 ptime_range
= perf_time__range_alloc(time_str
, &size
);
476 if (has_percent
|| reltime
) {
477 if (session
->evlist
->first_sample_time
== 0 &&
478 session
->evlist
->last_sample_time
== 0) {
479 pr_err("HINT: no first/last sample time found in perf data.\n"
480 "Please use latest perf binary to execute 'perf record'\n"
481 "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
487 num
= perf_time__percent_parse_str(
490 session
->evlist
->first_sample_time
,
491 session
->evlist
->last_sample_time
);
493 num
= perf_time__parse_strs(ptime_range
, time_str
, size
);
502 for (i
= 0; i
< num
; i
++) {
503 ptime_range
[i
].start
+= session
->evlist
->first_sample_time
;
504 ptime_range
[i
].end
+= session
->evlist
->first_sample_time
;
510 *ranges
= ptime_range
;
514 pr_err("Invalid time string\n");
520 int perf_time__parse_for_ranges(const char *time_str
,
521 struct perf_session
*session
,
522 struct perf_time_interval
**ranges
,
523 int *range_size
, int *range_num
)
525 return perf_time__parse_for_ranges_reltime(time_str
, session
, ranges
,
526 range_size
, range_num
, false);
529 int timestamp__scnprintf_usec(u64 timestamp
, char *buf
, size_t sz
)
531 u64 sec
= timestamp
/ NSEC_PER_SEC
;
532 u64 usec
= (timestamp
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
534 return scnprintf(buf
, sz
, "%"PRIu64
".%06"PRIu64
, sec
, usec
);
537 int timestamp__scnprintf_nsec(u64 timestamp
, char *buf
, size_t sz
)
539 u64 sec
= timestamp
/ NSEC_PER_SEC
,
540 nsec
= timestamp
% NSEC_PER_SEC
;
542 return scnprintf(buf
, sz
, "%" PRIu64
".%09" PRIu64
, sec
, nsec
);
545 int fetch_current_timestamp(char *buf
, size_t sz
)
551 if (gettimeofday(&tv
, NULL
) || !localtime_r(&tv
.tv_sec
, &tm
))
554 if (!strftime(dt
, sizeof(dt
), "%Y%m%d%H%M%S", &tm
))
557 scnprintf(buf
, sz
, "%s%02u", dt
, (unsigned)tv
.tv_usec
/ 10000);