]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
c284d669 | 2 | #include <stdlib.h> |
fdf9dc4b DA |
3 | #include <string.h> |
4 | #include <sys/time.h> | |
c284d669 | 5 | #include <linux/time64.h> |
fdf9dc4b DA |
6 | #include <time.h> |
7 | #include <errno.h> | |
8 | #include <inttypes.h> | |
13a70f35 | 9 | #include <math.h> |
fdf9dc4b DA |
10 | |
11 | #include "perf.h" | |
12 | #include "debug.h" | |
13 | #include "time-utils.h" | |
c284d669 DA |
14 | |
15 | int parse_nsec_time(const char *str, u64 *ptime) | |
16 | { | |
17 | u64 time_sec, time_nsec; | |
18 | char *end; | |
19 | ||
20 | time_sec = strtoul(str, &end, 10); | |
21 | if (*end != '.' && *end != '\0') | |
22 | return -1; | |
23 | ||
24 | if (*end == '.') { | |
25 | int i; | |
26 | char nsec_buf[10]; | |
27 | ||
28 | if (strlen(++end) > 9) | |
29 | return -1; | |
30 | ||
31 | strncpy(nsec_buf, end, 9); | |
32 | nsec_buf[9] = '\0'; | |
33 | ||
34 | /* make it nsec precision */ | |
35 | for (i = strlen(nsec_buf); i < 9; i++) | |
36 | nsec_buf[i] = '0'; | |
37 | ||
38 | time_nsec = strtoul(nsec_buf, &end, 10); | |
39 | if (*end != '\0') | |
40 | return -1; | |
41 | } else | |
42 | time_nsec = 0; | |
43 | ||
44 | *ptime = time_sec * NSEC_PER_SEC + time_nsec; | |
45 | return 0; | |
46 | } | |
fdf9dc4b DA |
47 | |
48 | static int parse_timestr_sec_nsec(struct perf_time_interval *ptime, | |
49 | char *start_str, char *end_str) | |
50 | { | |
51 | if (start_str && (*start_str != '\0') && | |
52 | (parse_nsec_time(start_str, &ptime->start) != 0)) { | |
53 | return -1; | |
54 | } | |
55 | ||
56 | if (end_str && (*end_str != '\0') && | |
57 | (parse_nsec_time(end_str, &ptime->end) != 0)) { | |
58 | return -1; | |
59 | } | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
13a70f35 | 64 | static int split_start_end(char **start, char **end, const char *ostr, char ch) |
fdf9dc4b DA |
65 | { |
66 | char *start_str, *end_str; | |
67 | char *d, *str; | |
fdf9dc4b DA |
68 | |
69 | if (ostr == NULL || *ostr == '\0') | |
70 | return 0; | |
71 | ||
72 | /* copy original string because we need to modify it */ | |
73 | str = strdup(ostr); | |
74 | if (str == NULL) | |
75 | return -ENOMEM; | |
76 | ||
fdf9dc4b | 77 | start_str = str; |
13a70f35 | 78 | d = strchr(start_str, ch); |
fdf9dc4b DA |
79 | if (d) { |
80 | *d = '\0'; | |
81 | ++d; | |
82 | } | |
83 | end_str = d; | |
84 | ||
13a70f35 JY |
85 | *start = start_str; |
86 | *end = end_str; | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
91 | int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr) | |
92 | { | |
93 | char *start_str = NULL, *end_str; | |
94 | int rc; | |
95 | ||
96 | rc = split_start_end(&start_str, &end_str, ostr, ','); | |
97 | if (rc || !start_str) | |
98 | return rc; | |
99 | ||
100 | ptime->start = 0; | |
101 | ptime->end = 0; | |
102 | ||
fdf9dc4b DA |
103 | rc = parse_timestr_sec_nsec(ptime, start_str, end_str); |
104 | ||
13a70f35 | 105 | free(start_str); |
fdf9dc4b DA |
106 | |
107 | /* make sure end time is after start time if it was given */ | |
108 | if (rc == 0 && ptime->end && ptime->end < ptime->start) | |
109 | return -EINVAL; | |
110 | ||
111 | pr_debug("start time %" PRIu64 ", ", ptime->start); | |
112 | pr_debug("end time %" PRIu64 "\n", ptime->end); | |
113 | ||
114 | return rc; | |
115 | } | |
116 | ||
13a70f35 JY |
117 | static int parse_percent(double *pcnt, char *str) |
118 | { | |
6e761cbc JY |
119 | char *c, *endptr; |
120 | double d; | |
13a70f35 JY |
121 | |
122 | c = strchr(str, '%'); | |
123 | if (c) | |
124 | *c = '\0'; | |
125 | else | |
126 | return -1; | |
127 | ||
6e761cbc JY |
128 | d = strtod(str, &endptr); |
129 | if (endptr != str + strlen(str)) | |
130 | return -1; | |
13a70f35 | 131 | |
6e761cbc | 132 | *pcnt = d / 100.0; |
13a70f35 JY |
133 | return 0; |
134 | } | |
135 | ||
136 | static int percent_slash_split(char *str, struct perf_time_interval *ptime, | |
137 | u64 start, u64 end) | |
138 | { | |
139 | char *p, *end_str; | |
140 | double pcnt, start_pcnt, end_pcnt; | |
141 | u64 total = end - start; | |
142 | int i; | |
143 | ||
144 | /* | |
145 | * Example: | |
146 | * 10%/2: select the second 10% slice and the third 10% slice | |
147 | */ | |
148 | ||
149 | /* We can modify this string since the original one is copied */ | |
150 | p = strchr(str, '/'); | |
151 | if (!p) | |
152 | return -1; | |
153 | ||
154 | *p = '\0'; | |
155 | if (parse_percent(&pcnt, str) < 0) | |
156 | return -1; | |
157 | ||
158 | p++; | |
159 | i = (int)strtol(p, &end_str, 10); | |
160 | if (*end_str) | |
161 | return -1; | |
162 | ||
163 | if (pcnt <= 0.0) | |
164 | return -1; | |
165 | ||
166 | start_pcnt = pcnt * (i - 1); | |
167 | end_pcnt = pcnt * i; | |
168 | ||
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; | |
178 | } | |
179 | ||
180 | static int percent_dash_split(char *str, struct perf_time_interval *ptime, | |
181 | u64 start, u64 end) | |
182 | { | |
183 | char *start_str = NULL, *end_str; | |
184 | double start_pcnt, end_pcnt; | |
185 | u64 total = end - start; | |
186 | int ret; | |
187 | ||
188 | /* | |
189 | * Example: 0%-10% | |
190 | */ | |
191 | ||
192 | ret = split_start_end(&start_str, &end_str, str, '-'); | |
193 | if (ret || !start_str) | |
194 | return ret; | |
195 | ||
196 | if ((parse_percent(&start_pcnt, start_str) != 0) || | |
197 | (parse_percent(&end_pcnt, end_str) != 0)) { | |
198 | free(start_str); | |
199 | return -1; | |
200 | } | |
201 | ||
202 | free(start_str); | |
203 | ||
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; | |
214 | } | |
215 | ||
216 | typedef int (*time_pecent_split)(char *, struct perf_time_interval *, | |
217 | u64 start, u64 end); | |
218 | ||
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) | |
222 | { | |
223 | char *str, *p1, *p2; | |
224 | int len, ret, i = 0; | |
225 | ||
226 | str = strdup(ostr); | |
227 | if (str == NULL) | |
228 | return -ENOMEM; | |
229 | ||
230 | len = strlen(str); | |
231 | p1 = str; | |
232 | ||
233 | while (p1 < str + len) { | |
234 | if (i >= num) { | |
235 | free(str); | |
236 | return -1; | |
237 | } | |
238 | ||
239 | p2 = strchr(p1, ','); | |
240 | if (p2) | |
241 | *p2 = '\0'; | |
242 | ||
243 | ret = (func)(p1, &ptime_buf[i], start, end); | |
244 | if (ret < 0) { | |
245 | free(str); | |
246 | return -1; | |
247 | } | |
248 | ||
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); | |
251 | ||
252 | i++; | |
253 | ||
254 | if (p2) | |
255 | p1 = p2 + 1; | |
256 | else | |
257 | break; | |
258 | } | |
259 | ||
260 | free(str); | |
261 | return i; | |
262 | } | |
263 | ||
3002812e JY |
264 | static int one_percent_convert(struct perf_time_interval *ptime_buf, |
265 | const char *ostr, u64 start, u64 end, char *c) | |
266 | { | |
267 | char *str; | |
268 | int len = strlen(ostr), ret; | |
269 | ||
270 | /* | |
271 | * c points to '%'. | |
272 | * '%' should be the last character | |
273 | */ | |
274 | if (ostr + len - 1 != c) | |
275 | return -1; | |
276 | ||
277 | /* | |
278 | * Construct a string like "xx%/1" | |
279 | */ | |
280 | str = malloc(len + 3); | |
281 | if (str == NULL) | |
282 | return -ENOMEM; | |
283 | ||
284 | memcpy(str, ostr, len); | |
285 | strcpy(str + len, "/1"); | |
286 | ||
287 | ret = percent_slash_split(str, ptime_buf, start, end); | |
288 | if (ret == 0) | |
289 | ret = 1; | |
290 | ||
291 | free(str); | |
292 | return ret; | |
293 | } | |
294 | ||
13a70f35 JY |
295 | int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, |
296 | const char *ostr, u64 start, u64 end) | |
297 | { | |
298 | char *c; | |
299 | ||
300 | /* | |
301 | * ostr example: | |
302 | * 10%/2,10%/3: select the second 10% slice and the third 10% slice | |
303 | * 0%-10%,30%-40%: multiple time range | |
3002812e | 304 | * 50%: just one percent |
13a70f35 JY |
305 | */ |
306 | ||
307 | memset(ptime_buf, 0, sizeof(*ptime_buf) * num); | |
308 | ||
309 | c = strchr(ostr, '/'); | |
310 | if (c) { | |
311 | return percent_comma_split(ptime_buf, num, ostr, start, | |
312 | end, percent_slash_split); | |
313 | } | |
314 | ||
315 | c = strchr(ostr, '-'); | |
316 | if (c) { | |
317 | return percent_comma_split(ptime_buf, num, ostr, start, | |
318 | end, percent_dash_split); | |
319 | } | |
320 | ||
3002812e JY |
321 | c = strchr(ostr, '%'); |
322 | if (c) | |
323 | return one_percent_convert(ptime_buf, ostr, start, end, c); | |
324 | ||
13a70f35 JY |
325 | return -1; |
326 | } | |
327 | ||
5a031f88 JY |
328 | struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size) |
329 | { | |
330 | const char *p1, *p2; | |
331 | int i = 1; | |
332 | struct perf_time_interval *ptime; | |
333 | ||
334 | /* | |
335 | * At least allocate one time range. | |
336 | */ | |
337 | if (!ostr) | |
338 | goto alloc; | |
339 | ||
340 | p1 = ostr; | |
341 | while (p1 < ostr + strlen(ostr)) { | |
342 | p2 = strchr(p1, ','); | |
343 | if (!p2) | |
344 | break; | |
345 | ||
346 | p1 = p2 + 1; | |
347 | i++; | |
348 | } | |
349 | ||
350 | alloc: | |
351 | *size = i; | |
352 | ptime = calloc(i, sizeof(*ptime)); | |
353 | return ptime; | |
354 | } | |
355 | ||
fdf9dc4b DA |
356 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp) |
357 | { | |
358 | /* if time is not set don't drop sample */ | |
359 | if (timestamp == 0) | |
360 | return false; | |
361 | ||
362 | /* otherwise compare sample time to time window */ | |
363 | if ((ptime->start && timestamp < ptime->start) || | |
364 | (ptime->end && timestamp > ptime->end)) { | |
365 | return true; | |
366 | } | |
367 | ||
368 | return false; | |
369 | } | |
c5e4027e | 370 | |
9a9b8b4b JY |
371 | bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, |
372 | int num, u64 timestamp) | |
373 | { | |
374 | struct perf_time_interval *ptime; | |
375 | int i; | |
376 | ||
377 | if ((timestamp == 0) || (num == 0)) | |
378 | return false; | |
379 | ||
380 | if (num == 1) | |
381 | return perf_time__skip_sample(&ptime_buf[0], timestamp); | |
382 | ||
383 | /* | |
384 | * start/end of multiple time ranges must be valid. | |
385 | */ | |
386 | for (i = 0; i < num; i++) { | |
387 | ptime = &ptime_buf[i]; | |
388 | ||
389 | if (timestamp >= ptime->start && | |
390 | ((timestamp < ptime->end && i < num - 1) || | |
391 | (timestamp <= ptime->end && i == num - 1))) { | |
392 | break; | |
393 | } | |
394 | } | |
395 | ||
396 | return (i == num) ? true : false; | |
397 | } | |
398 | ||
c5e4027e ACM |
399 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) |
400 | { | |
401 | u64 sec = timestamp / NSEC_PER_SEC; | |
402 | u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC; | |
403 | ||
404 | return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); | |
405 | } | |
406 | ||
407 | int fetch_current_timestamp(char *buf, size_t sz) | |
408 | { | |
409 | struct timeval tv; | |
410 | struct tm tm; | |
411 | char dt[32]; | |
412 | ||
413 | if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm)) | |
414 | return -1; | |
415 | ||
416 | if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm)) | |
417 | return -1; | |
418 | ||
419 | scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000); | |
420 | ||
421 | return 0; | |
422 | } |