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 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 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 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 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 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 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 } 423