1 // SPDX-License-Identifier: GPL-2.0
5 #include <linux/time64.h>
13 #include "time-utils.h"
15 int parse_nsec_time(const char *str
, u64
*ptime
)
17 u64 time_sec
, time_nsec
;
20 time_sec
= strtoul(str
, &end
, 10);
21 if (*end
!= '.' && *end
!= '\0')
28 if (strlen(++end
) > 9)
31 strncpy(nsec_buf
, end
, 9);
34 /* make it nsec precision */
35 for (i
= strlen(nsec_buf
); i
< 9; i
++)
38 time_nsec
= strtoul(nsec_buf
, &end
, 10);
44 *ptime
= time_sec
* NSEC_PER_SEC
+ time_nsec
;
48 static int parse_timestr_sec_nsec(struct perf_time_interval
*ptime
,
49 char *start_str
, char *end_str
)
51 if (start_str
&& (*start_str
!= '\0') &&
52 (parse_nsec_time(start_str
, &ptime
->start
) != 0)) {
56 if (end_str
&& (*end_str
!= '\0') &&
57 (parse_nsec_time(end_str
, &ptime
->end
) != 0)) {
64 static int split_start_end(char **start
, char **end
, const char *ostr
, char ch
)
66 char *start_str
, *end_str
;
69 if (ostr
== NULL
|| *ostr
== '\0')
72 /* copy original string because we need to modify it */
78 d
= strchr(start_str
, ch
);
91 int perf_time__parse_str(struct perf_time_interval
*ptime
, const char *ostr
)
93 char *start_str
= NULL
, *end_str
;
96 rc
= split_start_end(&start_str
, &end_str
, ostr
, ',');
103 rc
= parse_timestr_sec_nsec(ptime
, start_str
, end_str
);
107 /* make sure end time is after start time if it was given */
108 if (rc
== 0 && ptime
->end
&& ptime
->end
< ptime
->start
)
111 pr_debug("start time %" PRIu64
", ", ptime
->start
);
112 pr_debug("end time %" PRIu64
"\n", ptime
->end
);
117 static int parse_percent(double *pcnt
, char *str
)
122 c
= strchr(str
, '%');
128 d
= strtod(str
, &endptr
);
129 if (endptr
!= str
+ strlen(str
))
136 static int percent_slash_split(char *str
, struct perf_time_interval
*ptime
,
140 double pcnt
, start_pcnt
, end_pcnt
;
141 u64 total
= end
- start
;
146 * 10%/2: select the second 10% slice and the third 10% slice
149 /* We can modify this string since the original one is copied */
150 p
= strchr(str
, '/');
155 if (parse_percent(&pcnt
, str
) < 0)
159 i
= (int)strtol(p
, &end_str
, 10);
166 start_pcnt
= pcnt
* (i
- 1);
169 if (start_pcnt
< 0.0 || start_pcnt
> 1.0 ||
170 end_pcnt
< 0.0 || end_pcnt
> 1.0) {
174 ptime
->start
= start
+ round(start_pcnt
* total
);
175 ptime
->end
= start
+ round(end_pcnt
* total
);
180 static int percent_dash_split(char *str
, struct perf_time_interval
*ptime
,
183 char *start_str
= NULL
, *end_str
;
184 double start_pcnt
, end_pcnt
;
185 u64 total
= end
- start
;
192 ret
= split_start_end(&start_str
, &end_str
, str
, '-');
193 if (ret
|| !start_str
)
196 if ((parse_percent(&start_pcnt
, start_str
) != 0) ||
197 (parse_percent(&end_pcnt
, end_str
) != 0)) {
204 if (start_pcnt
< 0.0 || start_pcnt
> 1.0 ||
205 end_pcnt
< 0.0 || end_pcnt
> 1.0 ||
206 start_pcnt
> end_pcnt
) {
210 ptime
->start
= start
+ round(start_pcnt
* total
);
211 ptime
->end
= start
+ round(end_pcnt
* total
);
216 typedef int (*time_pecent_split
)(char *, struct perf_time_interval
*,
219 static int percent_comma_split(struct perf_time_interval
*ptime_buf
, int num
,
220 const char *ostr
, u64 start
, u64 end
,
221 time_pecent_split func
)
233 while (p1
< str
+ len
) {
239 p2
= strchr(p1
, ',');
243 ret
= (func
)(p1
, &ptime_buf
[i
], start
, end
);
249 pr_debug("start time %d: %" PRIu64
", ", i
, ptime_buf
[i
].start
);
250 pr_debug("end time %d: %" PRIu64
"\n", i
, ptime_buf
[i
].end
);
264 static int one_percent_convert(struct perf_time_interval
*ptime_buf
,
265 const char *ostr
, u64 start
, u64 end
, char *c
)
268 int len
= strlen(ostr
), ret
;
272 * '%' should be the last character
274 if (ostr
+ len
- 1 != c
)
278 * Construct a string like "xx%/1"
280 str
= malloc(len
+ 3);
284 memcpy(str
, ostr
, len
);
285 strcpy(str
+ len
, "/1");
287 ret
= percent_slash_split(str
, ptime_buf
, start
, end
);
295 int perf_time__percent_parse_str(struct perf_time_interval
*ptime_buf
, int num
,
296 const char *ostr
, u64 start
, u64 end
)
302 * 10%/2,10%/3: select the second 10% slice and the third 10% slice
303 * 0%-10%,30%-40%: multiple time range
304 * 50%: just one percent
307 memset(ptime_buf
, 0, sizeof(*ptime_buf
) * num
);
309 c
= strchr(ostr
, '/');
311 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
312 end
, percent_slash_split
);
315 c
= strchr(ostr
, '-');
317 return percent_comma_split(ptime_buf
, num
, ostr
, start
,
318 end
, percent_dash_split
);
321 c
= strchr(ostr
, '%');
323 return one_percent_convert(ptime_buf
, ostr
, start
, end
, c
);
328 struct perf_time_interval
*perf_time__range_alloc(const char *ostr
, int *size
)
332 struct perf_time_interval
*ptime
;
335 * At least allocate one time range.
341 while (p1
< ostr
+ strlen(ostr
)) {
342 p2
= strchr(p1
, ',');
352 ptime
= calloc(i
, sizeof(*ptime
));
356 bool perf_time__skip_sample(struct perf_time_interval
*ptime
, u64 timestamp
)
358 /* if time is not set don't drop sample */
362 /* otherwise compare sample time to time window */
363 if ((ptime
->start
&& timestamp
< ptime
->start
) ||
364 (ptime
->end
&& timestamp
> ptime
->end
)) {
371 bool perf_time__ranges_skip_sample(struct perf_time_interval
*ptime_buf
,
372 int num
, u64 timestamp
)
374 struct perf_time_interval
*ptime
;
377 if ((timestamp
== 0) || (num
== 0))
381 return perf_time__skip_sample(&ptime_buf
[0], timestamp
);
384 * start/end of multiple time ranges must be valid.
386 for (i
= 0; i
< num
; i
++) {
387 ptime
= &ptime_buf
[i
];
389 if (timestamp
>= ptime
->start
&&
390 ((timestamp
< ptime
->end
&& i
< num
- 1) ||
391 (timestamp
<= ptime
->end
&& i
== num
- 1))) {
396 return (i
== num
) ? true : false;
399 int timestamp__scnprintf_usec(u64 timestamp
, char *buf
, size_t sz
)
401 u64 sec
= timestamp
/ NSEC_PER_SEC
;
402 u64 usec
= (timestamp
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
404 return scnprintf(buf
, sz
, "%"PRIu64
".%06"PRIu64
, sec
, usec
);
407 int fetch_current_timestamp(char *buf
, size_t sz
)
413 if (gettimeofday(&tv
, NULL
) || !localtime_r(&tv
.tv_sec
, &tm
))
416 if (!strftime(dt
, sizeof(dt
), "%Y%m%d%H%M%S", &tm
))
419 scnprintf(buf
, sz
, "%s%02u", dt
, (unsigned)tv
.tv_usec
/ 10000);