Linux Perf
time-utils.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/time.h>
5 #include <linux/time64.h>
6 #include <time.h>
7 #include <errno.h>
8 #include <inttypes.h>
9 #include <math.h>
10 
11 #include "perf.h"
12 #include "debug.h"
13 #include "time-utils.h"
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 }
47 
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 
64 static int split_start_end(char **start, char **end, const char *ostr, char ch)
65 {
66  char *start_str, *end_str;
67  char *d, *str;
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 
77  start_str = str;
78  d = strchr(start_str, ch);
79  if (d) {
80  *d = '\0';
81  ++d;
82  }
83  end_str = d;
84 
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 
103  rc = parse_timestr_sec_nsec(ptime, start_str, end_str);
104 
105  free(start_str);
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 
117 static int parse_percent(double *pcnt, char *str)
118 {
119  char *c, *endptr;
120  double d;
121 
122  c = strchr(str, '%');
123  if (c)
124  *c = '\0';
125  else
126  return -1;
127 
128  d = strtod(str, &endptr);
129  if (endptr != str + strlen(str))
130  return -1;
131 
132  *pcnt = d / 100.0;
133  return 0;
134 }
135 
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,
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 
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 
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
304  * 50%: just one percent
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 
321  c = strchr(ostr, '%');
322  if (c)
323  return one_percent_convert(ptime_buf, ostr, start, end, c);
324 
325  return -1;
326 }
327 
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 
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 }
370 
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 
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 }
static int one_percent_convert(struct perf_time_interval *ptime_buf, const char *ostr, u64 start, u64 end, char *c)
Definition: time-utils.c:264
static int parse_percent(double *pcnt, char *str)
Definition: time-utils.c:117
size_t size
Definition: evsel.c:60
int(* func)(void)
Definition: clang.c:9
static struct perf_time_interval ptime
Definition: builtin-kmem.c:77
int parse_nsec_time(const char *str, u64 *ptime)
Definition: time-utils.c:15
static int percent_comma_split(struct perf_time_interval *ptime_buf, int num, const char *ostr, u64 start, u64 end, time_pecent_split func)
Definition: time-utils.c:219
struct perf_time_interval * perf_time__range_alloc(const char *ostr, int *size)
Definition: time-utils.c:328
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
Definition: time-utils.c:399
bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
Definition: time-utils.c:356
int(* time_pecent_split)(char *, struct perf_time_interval *, u64 start, u64 end)
Definition: time-utils.c:216
x86 movsq based memset() in arch/x86/lib/memset_64.S") MEMSET_FN(memset_erms
bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, int num, u64 timestamp)
Definition: time-utils.c:371
void * malloc(YYSIZE_T)
#define pr_debug(fmt,...)
Definition: json.h:27
int int long sec
Definition: 5sec.c:45
static int str(yyscan_t scanner, int token)
int fetch_current_timestamp(char *buf, size_t sz)
Definition: time-utils.c:407
static int parse_timestr_sec_nsec(struct perf_time_interval *ptime, char *start_str, char *end_str)
Definition: time-utils.c:48
static int percent_dash_split(char *str, struct perf_time_interval *ptime, u64 start, u64 end)
Definition: time-utils.c:180
x86 movsq based memcpy() in arch/x86/lib/memcpy_64.S") MEMCPY_FN(memcpy_erms
#define NSEC_PER_SEC
Definition: jvmti_agent.c:101
static int split_start_end(char **start, char **end, const char *ostr, char ch)
Definition: time-utils.c:64
u64 start
Definition: hists_common.c:25
int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, const char *ostr, u64 start, u64 end)
Definition: time-utils.c:295
void free(void *)
static int percent_slash_split(char *str, struct perf_time_interval *ptime, u64 start, u64 end)
Definition: time-utils.c:136
int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
Definition: time-utils.c:91