1 // SPDX-License-Identifier: GPL-2.0
4 #include <linux/string.h>
6 #include <linux/time64.h>
11 #include <linux/ctype.h>
14 #include "time-utils.h"
18 int parse_nsec_time(const char *str
, u64
*ptime
)
20 u64 time_sec
, time_nsec
;
23 time_sec
= strtoul(str
, &end
, 10);
24 if (*end
!= '.' && *end
!= '\0')
31 if (strlen(++end
) > 9)
34 strncpy(nsec_buf
, end
, 9);
37 /* make it nsec precision */
38 for (i
= strlen(nsec_buf
); i
< 9; i
++)
41 time_nsec
= strtoul(nsec_buf
, &end
, 10);
47 *ptime
= time_sec
* NSEC_PER_SEC
+ time_nsec
;
51 static int parse_timestr_sec_nsec(struct perf_time_interval
*ptime
,
52 char *start_str
, char *end_str
)
54 if (start_str
&& (*start_str
!= '\0') &&
55 (parse_nsec_time(start_str
, &ptime
->start
) != 0)) {
59 if (end_str
&& (*end_str
!= '\0') &&
60 (parse_nsec_time(end_str
, &ptime
->end
) != 0)) {
67 static int split_start_end(char **start
, char **end
, const char *ostr
, char ch
)
69 char *start_str
, *end_str
;
72 if (ostr
== NULL
|| *ostr
== '\0')
75 /* copy original string because we need to modify it */
81 d
= strchr(start_str
, ch
);
94 int perf_time__parse_str(struct perf_time_interval
*ptime
, const char *ostr
)
96 char *start_str
= NULL
, *end_str
;
99 rc
= split_start_end(&start_str
, &end_str
, ostr
, ',');
100 if (rc
|| !start_str
)
106 rc
= parse_timestr_sec_nsec(ptime
, start_str
, end_str
);
110 /* make sure end time is after start time if it was given */
111 if (rc
== 0 && ptime
->end
&& ptime
->end
< ptime
->start
)
114 pr_debug("start time %" PRIu64
", ", ptime
->start
);
115 pr_debug("end time %" PRIu64
"\n", ptime
->end
);
120 static int perf_time__parse_strs(struct perf_time_interval
*ptime
,
121 const char *ostr
, int size
)
125 int i
, num
= 0, rc
= 0;
127 /* Count the commas */
128 for (cp
= ostr
; *cp
; cp
++)
129 num
+= !!(*cp
== ',');
140 /* Split the string and parse each piece, except the last */
141 for (i
= 0, p
= str
; i
< num
- 1; i
++) {
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
)) {
152 /* Split and parse */
155 rc
= perf_time__parse_str(ptime
+ i
, arg
);
160 /* Parse the last piece */
161 rc
= perf_time__parse_str(ptime
+ i
, p
);
165 /* Check there is no overlap */
166 for (i
= 0; i
< num
- 1; i
++) {
167 if (ptime
[i
].end
>= ptime
[i
+ 1].start
) {
180 static int parse_percent(double *pcnt
, char *str
)
185 c
= strchr(str
, '%');
191 d
= strtod(str
, &endptr
);
192 if (endptr
!= str
+ strlen(str
))
199 static int set_percent_time(struct perf_time_interval
*ptime
, double start_pcnt
,
200 double end_pcnt
, u64 start
, u64 end
)
202 u64 total
= end
- start
;
204 if (start_pcnt
< 0.0 || start_pcnt
> 1.0 ||
205 end_pcnt
< 0.0 || end_pcnt
> 1.0) {
209 ptime
->start
= start
+ round(start_pcnt
* total
);
210 ptime
->end
= start
+ round(end_pcnt
* total
);
212 if (ptime
->end
> ptime
->start
&& ptime
->end
!= end
)
218 static int percent_slash_split(char *str
, struct perf_time_interval
*ptime
,
222 double pcnt
, start_pcnt
, end_pcnt
;
227 * 10%/2: select the second 10% slice and the third 10% slice
230 /* We can modify this string since the original one is copied */
231 p
= strchr(str
, '/');
236 if (parse_percent(&pcnt
, str
) < 0)
240 i
= (int)strtol(p
, &end_str
, 10);
247 start_pcnt
= pcnt
* (i
- 1);
250 return set_percent_time(ptime
, start_pcnt
, end_pcnt
, start
, end
);
253 static int percent_dash_split(char *str
, struct perf_time_interval
*ptime
,
256 char *start_str
= NULL
, *end_str
;
257 double start_pcnt
, end_pcnt
;
264 ret
= split_start_end(&start_str
, &end_str
, str
, '-');
265 if (ret
|| !start_str
)
268 if ((parse_percent(&start_pcnt
, start_str
) != 0) ||
269 (parse_percent(&end_pcnt
, end_str
) != 0)) {
276 return set_percent_time(ptime
, start_pcnt
, end_pcnt
, start
, end
);
279 typedef int (*time_pecent_split
)(char *, struct perf_time_interval
*,
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
)
296 while (p1
< str
+ len
) {
302 p2
= strchr(p1
, ',');
306 ret
= (func
)(p1
, &ptime_buf
[i
], start
, end
);
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
);
327 static int one_percent_convert(struct perf_time_interval
*ptime_buf
,
328 const char *ostr
, u64 start
, u64 end
, char *c
)
331 int len
= strlen(ostr
), ret
;
335 * '%' should be the last character
337 if (ostr
+ len
- 1 != c
)
341 * Construct a string like "xx%/1"
343 str
= malloc(len
+ 3);
347 memcpy(str
, ostr
, len
);
348 strcpy(str
+ len
, "/1");
350 ret
= percent_slash_split(str
, ptime_buf
, start
, end
);
358 int perf_time__percent_parse_str(struct perf_time_interval
*ptime_buf
, int num
,
359 const char *ostr
, u64 start
, u64 end
)
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
370 memset(ptime_buf
, 0, sizeof(*ptime_buf
) * num
);
372 c
= strchr(ostr
, '/');
374 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
375 end
, percent_slash_split
);
378 c
= strchr(ostr
, '-');
380 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
381 end
, percent_dash_split
);
384 c
= strchr(ostr
, '%');
386 return one_percent_convert(ptime_buf
, ostr
, start
, end
, c
);
391 struct perf_time_interval
*perf_time__range_alloc(const char *ostr
, int *size
)
395 struct perf_time_interval
*ptime
;
398 * At least allocate one time range.
404 while (p1
< ostr
+ strlen(ostr
)) {
405 p2
= strchr(p1
, ',');
415 ptime
= calloc(i
, sizeof(*ptime
));
419 bool perf_time__skip_sample(struct perf_time_interval
*ptime
, u64 timestamp
)
421 /* if time is not set don't drop sample */
425 /* otherwise compare sample time to time window */
426 if ((ptime
->start
&& timestamp
< ptime
->start
) ||
427 (ptime
->end
&& timestamp
> ptime
->end
)) {
434 bool perf_time__ranges_skip_sample(struct perf_time_interval
*ptime_buf
,
435 int num
, u64 timestamp
)
437 struct perf_time_interval
*ptime
;
440 if ((!ptime_buf
) || (timestamp
== 0) || (num
== 0))
444 return perf_time__skip_sample(&ptime_buf
[0], timestamp
);
447 * start/end of multiple time ranges must be valid.
449 for (i
= 0; i
< num
; i
++) {
450 ptime
= &ptime_buf
[i
];
452 if (timestamp
>= ptime
->start
&&
453 (timestamp
<= ptime
->end
|| !ptime
->end
)) {
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
,
467 bool has_percent
= strchr(time_str
, '%');
468 struct perf_time_interval
*ptime_range
;
469 int size
, num
, ret
= -EINVAL
;
471 ptime_range
= perf_time__range_alloc(time_str
, &size
);
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");
486 num
= perf_time__percent_parse_str(
489 session
->evlist
->first_sample_time
,
490 session
->evlist
->last_sample_time
);
492 num
= perf_time__parse_strs(ptime_range
, time_str
, size
);
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
;
509 *ranges
= ptime_range
;
513 pr_err("Invalid time string\n");
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
)
524 return perf_time__parse_for_ranges_reltime(time_str
, session
, ranges
,
525 range_size
, range_num
, false);
528 int timestamp__scnprintf_usec(u64 timestamp
, char *buf
, size_t sz
)
530 u64 sec
= timestamp
/ NSEC_PER_SEC
;
531 u64 usec
= (timestamp
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
533 return scnprintf(buf
, sz
, "%"PRIu64
".%06"PRIu64
, sec
, usec
);
536 int timestamp__scnprintf_nsec(u64 timestamp
, char *buf
, size_t sz
)
538 u64 sec
= timestamp
/ NSEC_PER_SEC
,
539 nsec
= timestamp
% NSEC_PER_SEC
;
541 return scnprintf(buf
, sz
, "%" PRIu64
".%09" PRIu64
, sec
, nsec
);
544 int fetch_current_timestamp(char *buf
, size_t sz
)
550 if (gettimeofday(&tv
, NULL
) || !localtime_r(&tv
.tv_sec
, &tm
))
553 if (!strftime(dt
, sizeof(dt
), "%Y%m%d%H%M%S", &tm
))
556 scnprintf(buf
, sz
, "%s%02u", dt
, (unsigned)tv
.tv_usec
/ 10000);