1 // SPDX-License-Identifier: GPL-2.0
5 #include <linux/time64.h>
13 #include "time-utils.h"
17 int parse_nsec_time(const char *str
, u64
*ptime
)
19 u64 time_sec
, time_nsec
;
22 time_sec
= strtoul(str
, &end
, 10);
23 if (*end
!= '.' && *end
!= '\0')
30 if (strlen(++end
) > 9)
33 strncpy(nsec_buf
, end
, 9);
36 /* make it nsec precision */
37 for (i
= strlen(nsec_buf
); i
< 9; i
++)
40 time_nsec
= strtoul(nsec_buf
, &end
, 10);
46 *ptime
= time_sec
* NSEC_PER_SEC
+ time_nsec
;
50 static int parse_timestr_sec_nsec(struct perf_time_interval
*ptime
,
51 char *start_str
, char *end_str
)
53 if (start_str
&& (*start_str
!= '\0') &&
54 (parse_nsec_time(start_str
, &ptime
->start
) != 0)) {
58 if (end_str
&& (*end_str
!= '\0') &&
59 (parse_nsec_time(end_str
, &ptime
->end
) != 0)) {
66 static int split_start_end(char **start
, char **end
, const char *ostr
, char ch
)
68 char *start_str
, *end_str
;
71 if (ostr
== NULL
|| *ostr
== '\0')
74 /* copy original string because we need to modify it */
80 d
= strchr(start_str
, ch
);
93 int perf_time__parse_str(struct perf_time_interval
*ptime
, const char *ostr
)
95 char *start_str
= NULL
, *end_str
;
98 rc
= split_start_end(&start_str
, &end_str
, ostr
, ',');
105 rc
= parse_timestr_sec_nsec(ptime
, start_str
, end_str
);
109 /* make sure end time is after start time if it was given */
110 if (rc
== 0 && ptime
->end
&& ptime
->end
< ptime
->start
)
113 pr_debug("start time %" PRIu64
", ", ptime
->start
);
114 pr_debug("end time %" PRIu64
"\n", ptime
->end
);
119 static int parse_percent(double *pcnt
, char *str
)
124 c
= strchr(str
, '%');
130 d
= strtod(str
, &endptr
);
131 if (endptr
!= str
+ strlen(str
))
138 static int percent_slash_split(char *str
, struct perf_time_interval
*ptime
,
142 double pcnt
, start_pcnt
, end_pcnt
;
143 u64 total
= end
- start
;
148 * 10%/2: select the second 10% slice and the third 10% slice
151 /* We can modify this string since the original one is copied */
152 p
= strchr(str
, '/');
157 if (parse_percent(&pcnt
, str
) < 0)
161 i
= (int)strtol(p
, &end_str
, 10);
168 start_pcnt
= pcnt
* (i
- 1);
171 if (start_pcnt
< 0.0 || start_pcnt
> 1.0 ||
172 end_pcnt
< 0.0 || end_pcnt
> 1.0) {
176 ptime
->start
= start
+ round(start_pcnt
* total
);
177 ptime
->end
= start
+ round(end_pcnt
* total
);
182 static int percent_dash_split(char *str
, struct perf_time_interval
*ptime
,
185 char *start_str
= NULL
, *end_str
;
186 double start_pcnt
, end_pcnt
;
187 u64 total
= end
- start
;
194 ret
= split_start_end(&start_str
, &end_str
, str
, '-');
195 if (ret
|| !start_str
)
198 if ((parse_percent(&start_pcnt
, start_str
) != 0) ||
199 (parse_percent(&end_pcnt
, end_str
) != 0)) {
206 if (start_pcnt
< 0.0 || start_pcnt
> 1.0 ||
207 end_pcnt
< 0.0 || end_pcnt
> 1.0 ||
208 start_pcnt
> end_pcnt
) {
212 ptime
->start
= start
+ round(start_pcnt
* total
);
213 ptime
->end
= start
+ round(end_pcnt
* total
);
218 typedef int (*time_pecent_split
)(char *, struct perf_time_interval
*,
221 static int percent_comma_split(struct perf_time_interval
*ptime_buf
, int num
,
222 const char *ostr
, u64 start
, u64 end
,
223 time_pecent_split func
)
235 while (p1
< str
+ len
) {
241 p2
= strchr(p1
, ',');
245 ret
= (func
)(p1
, &ptime_buf
[i
], start
, end
);
251 pr_debug("start time %d: %" PRIu64
", ", i
, ptime_buf
[i
].start
);
252 pr_debug("end time %d: %" PRIu64
"\n", i
, ptime_buf
[i
].end
);
266 static int one_percent_convert(struct perf_time_interval
*ptime_buf
,
267 const char *ostr
, u64 start
, u64 end
, char *c
)
270 int len
= strlen(ostr
), ret
;
274 * '%' should be the last character
276 if (ostr
+ len
- 1 != c
)
280 * Construct a string like "xx%/1"
282 str
= malloc(len
+ 3);
286 memcpy(str
, ostr
, len
);
287 strcpy(str
+ len
, "/1");
289 ret
= percent_slash_split(str
, ptime_buf
, start
, end
);
297 int perf_time__percent_parse_str(struct perf_time_interval
*ptime_buf
, int num
,
298 const char *ostr
, u64 start
, u64 end
)
304 * 10%/2,10%/3: select the second 10% slice and the third 10% slice
305 * 0%-10%,30%-40%: multiple time range
306 * 50%: just one percent
309 memset(ptime_buf
, 0, sizeof(*ptime_buf
) * num
);
311 c
= strchr(ostr
, '/');
313 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
314 end
, percent_slash_split
);
317 c
= strchr(ostr
, '-');
319 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
320 end
, percent_dash_split
);
323 c
= strchr(ostr
, '%');
325 return one_percent_convert(ptime_buf
, ostr
, start
, end
, c
);
330 struct perf_time_interval
*perf_time__range_alloc(const char *ostr
, int *size
)
334 struct perf_time_interval
*ptime
;
337 * At least allocate one time range.
343 while (p1
< ostr
+ strlen(ostr
)) {
344 p2
= strchr(p1
, ',');
354 ptime
= calloc(i
, sizeof(*ptime
));
358 bool perf_time__skip_sample(struct perf_time_interval
*ptime
, u64 timestamp
)
360 /* if time is not set don't drop sample */
364 /* otherwise compare sample time to time window */
365 if ((ptime
->start
&& timestamp
< ptime
->start
) ||
366 (ptime
->end
&& timestamp
> ptime
->end
)) {
373 bool perf_time__ranges_skip_sample(struct perf_time_interval
*ptime_buf
,
374 int num
, u64 timestamp
)
376 struct perf_time_interval
*ptime
;
379 if ((!ptime_buf
) || (timestamp
== 0) || (num
== 0))
383 return perf_time__skip_sample(&ptime_buf
[0], timestamp
);
386 * start/end of multiple time ranges must be valid.
388 for (i
= 0; i
< num
; i
++) {
389 ptime
= &ptime_buf
[i
];
391 if (timestamp
>= ptime
->start
&&
392 ((timestamp
< ptime
->end
&& i
< num
- 1) ||
393 (timestamp
<= ptime
->end
&& i
== num
- 1))) {
398 return (i
== num
) ? true : false;
401 int perf_time__parse_for_ranges(const char *time_str
,
402 struct perf_session
*session
,
403 struct perf_time_interval
**ranges
,
404 int *range_size
, int *range_num
)
406 struct perf_time_interval
*ptime_range
;
409 ptime_range
= perf_time__range_alloc(time_str
, &size
);
413 if (perf_time__parse_str(ptime_range
, time_str
) != 0) {
414 if (session
->evlist
->first_sample_time
== 0 &&
415 session
->evlist
->last_sample_time
== 0) {
416 pr_err("HINT: no first/last sample time found in perf data.\n"
417 "Please use latest perf binary to execute 'perf record'\n"
418 "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
423 num
= perf_time__percent_parse_str(
426 session
->evlist
->first_sample_time
,
427 session
->evlist
->last_sample_time
);
430 pr_err("Invalid time string\n");
440 *ranges
= ptime_range
;
448 int timestamp__scnprintf_usec(u64 timestamp
, char *buf
, size_t sz
)
450 u64 sec
= timestamp
/ NSEC_PER_SEC
;
451 u64 usec
= (timestamp
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
453 return scnprintf(buf
, sz
, "%"PRIu64
".%06"PRIu64
, sec
, usec
);
456 int timestamp__scnprintf_nsec(u64 timestamp
, char *buf
, size_t sz
)
458 u64 sec
= timestamp
/ NSEC_PER_SEC
,
459 nsec
= timestamp
% NSEC_PER_SEC
;
461 return scnprintf(buf
, sz
, "%" PRIu64
".%09" PRIu64
, sec
, nsec
);
464 int fetch_current_timestamp(char *buf
, size_t sz
)
470 if (gettimeofday(&tv
, NULL
) || !localtime_r(&tv
.tv_sec
, &tm
))
473 if (!strftime(dt
, sizeof(dt
), "%Y%m%d%H%M%S", &tm
))
476 scnprintf(buf
, sz
, "%s%02u", dt
, (unsigned)tv
.tv_usec
/ 10000);