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 #include "session.h" 15 #include "evlist.h" 16 17 int parse_nsec_time(const char *str, u64 *ptime) 18 { 19 u64 time_sec, time_nsec; 20 char *end; 21 22 time_sec = strtoul(str, &end, 10); 23 if (*end != '.' && *end != '\0') 24 return -1; 25 26 if (*end == '.') { 27 int i; 28 char nsec_buf[10]; 29 30 if (strlen(++end) > 9) 31 return -1; 32 33 strncpy(nsec_buf, end, 9); 34 nsec_buf[9] = '\0'; 35 36 /* make it nsec precision */ 37 for (i = strlen(nsec_buf); i < 9; i++) 38 nsec_buf[i] = '0'; 39 40 time_nsec = strtoul(nsec_buf, &end, 10); 41 if (*end != '\0') 42 return -1; 43 } else 44 time_nsec = 0; 45 46 *ptime = time_sec * NSEC_PER_SEC + time_nsec; 47 return 0; 48 } 49 50 static int parse_timestr_sec_nsec(struct perf_time_interval *ptime, 51 char *start_str, char *end_str) 52 { 53 if (start_str && (*start_str != '\0') && 54 (parse_nsec_time(start_str, &ptime->start) != 0)) { 55 return -1; 56 } 57 58 if (end_str && (*end_str != '\0') && 59 (parse_nsec_time(end_str, &ptime->end) != 0)) { 60 return -1; 61 } 62 63 return 0; 64 } 65 66 static int split_start_end(char **start, char **end, const char *ostr, char ch) 67 { 68 char *start_str, *end_str; 69 char *d, *str; 70 71 if (ostr == NULL || *ostr == '\0') 72 return 0; 73 74 /* copy original string because we need to modify it */ 75 str = strdup(ostr); 76 if (str == NULL) 77 return -ENOMEM; 78 79 start_str = str; 80 d = strchr(start_str, ch); 81 if (d) { 82 *d = '\0'; 83 ++d; 84 } 85 end_str = d; 86 87 *start = start_str; 88 *end = end_str; 89 90 return 0; 91 } 92 93 int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr) 94 { 95 char *start_str = NULL, *end_str; 96 int rc; 97 98 rc = split_start_end(&start_str, &end_str, ostr, ','); 99 if (rc || !start_str) 100 return rc; 101 102 ptime->start = 0; 103 ptime->end = 0; 104 105 rc = parse_timestr_sec_nsec(ptime, start_str, end_str); 106 107 free(start_str); 108 109 /* make sure end time is after start time if it was given */ 110 if (rc == 0 && ptime->end && ptime->end < ptime->start) 111 return -EINVAL; 112 113 pr_debug("start time %" PRIu64 ", ", ptime->start); 114 pr_debug("end time %" PRIu64 "\n", ptime->end); 115 116 return rc; 117 } 118 119 static int parse_percent(double *pcnt, char *str) 120 { 121 char *c, *endptr; 122 double d; 123 124 c = strchr(str, '%'); 125 if (c) 126 *c = '\0'; 127 else 128 return -1; 129 130 d = strtod(str, &endptr); 131 if (endptr != str + strlen(str)) 132 return -1; 133 134 *pcnt = d / 100.0; 135 return 0; 136 } 137 138 static int percent_slash_split(char *str, struct perf_time_interval *ptime, 139 u64 start, u64 end) 140 { 141 char *p, *end_str; 142 double pcnt, start_pcnt, end_pcnt; 143 u64 total = end - start; 144 int i; 145 146 /* 147 * Example: 148 * 10%/2: select the second 10% slice and the third 10% slice 149 */ 150 151 /* We can modify this string since the original one is copied */ 152 p = strchr(str, '/'); 153 if (!p) 154 return -1; 155 156 *p = '\0'; 157 if (parse_percent(&pcnt, str) < 0) 158 return -1; 159 160 p++; 161 i = (int)strtol(p, &end_str, 10); 162 if (*end_str) 163 return -1; 164 165 if (pcnt <= 0.0) 166 return -1; 167 168 start_pcnt = pcnt * (i - 1); 169 end_pcnt = pcnt * i; 170 171 if (start_pcnt < 0.0 || start_pcnt > 1.0 || 172 end_pcnt < 0.0 || end_pcnt > 1.0) { 173 return -1; 174 } 175 176 ptime->start = start + round(start_pcnt * total); 177 ptime->end = start + round(end_pcnt * total); 178 179 return 0; 180 } 181 182 static int percent_dash_split(char *str, struct perf_time_interval *ptime, 183 u64 start, u64 end) 184 { 185 char *start_str = NULL, *end_str; 186 double start_pcnt, end_pcnt; 187 u64 total = end - start; 188 int ret; 189 190 /* 191 * Example: 0%-10% 192 */ 193 194 ret = split_start_end(&start_str, &end_str, str, '-'); 195 if (ret || !start_str) 196 return ret; 197 198 if ((parse_percent(&start_pcnt, start_str) != 0) || 199 (parse_percent(&end_pcnt, end_str) != 0)) { 200 free(start_str); 201 return -1; 202 } 203 204 free(start_str); 205 206 if (start_pcnt < 0.0 || start_pcnt > 1.0 || 207 end_pcnt < 0.0 || end_pcnt > 1.0 || 208 start_pcnt > end_pcnt) { 209 return -1; 210 } 211 212 ptime->start = start + round(start_pcnt * total); 213 ptime->end = start + round(end_pcnt * total); 214 215 return 0; 216 } 217 218 typedef int (*time_pecent_split)(char *, struct perf_time_interval *, 219 u64 start, u64 end); 220 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) 224 { 225 char *str, *p1, *p2; 226 int len, ret, i = 0; 227 228 str = strdup(ostr); 229 if (str == NULL) 230 return -ENOMEM; 231 232 len = strlen(str); 233 p1 = str; 234 235 while (p1 < str + len) { 236 if (i >= num) { 237 free(str); 238 return -1; 239 } 240 241 p2 = strchr(p1, ','); 242 if (p2) 243 *p2 = '\0'; 244 245 ret = (func)(p1, &ptime_buf[i], start, end); 246 if (ret < 0) { 247 free(str); 248 return -1; 249 } 250 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); 253 254 i++; 255 256 if (p2) 257 p1 = p2 + 1; 258 else 259 break; 260 } 261 262 free(str); 263 return i; 264 } 265 266 static int one_percent_convert(struct perf_time_interval *ptime_buf, 267 const char *ostr, u64 start, u64 end, char *c) 268 { 269 char *str; 270 int len = strlen(ostr), ret; 271 272 /* 273 * c points to '%'. 274 * '%' should be the last character 275 */ 276 if (ostr + len - 1 != c) 277 return -1; 278 279 /* 280 * Construct a string like "xx%/1" 281 */ 282 str = malloc(len + 3); 283 if (str == NULL) 284 return -ENOMEM; 285 286 memcpy(str, ostr, len); 287 strcpy(str + len, "/1"); 288 289 ret = percent_slash_split(str, ptime_buf, start, end); 290 if (ret == 0) 291 ret = 1; 292 293 free(str); 294 return ret; 295 } 296 297 int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, 298 const char *ostr, u64 start, u64 end) 299 { 300 char *c; 301 302 /* 303 * ostr example: 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 307 */ 308 309 memset(ptime_buf, 0, sizeof(*ptime_buf) * num); 310 311 c = strchr(ostr, '/'); 312 if (c) { 313 return percent_comma_split(ptime_buf, num, ostr, start, 314 end, percent_slash_split); 315 } 316 317 c = strchr(ostr, '-'); 318 if (c) { 319 return percent_comma_split(ptime_buf, num, ostr, start, 320 end, percent_dash_split); 321 } 322 323 c = strchr(ostr, '%'); 324 if (c) 325 return one_percent_convert(ptime_buf, ostr, start, end, c); 326 327 return -1; 328 } 329 330 struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size) 331 { 332 const char *p1, *p2; 333 int i = 1; 334 struct perf_time_interval *ptime; 335 336 /* 337 * At least allocate one time range. 338 */ 339 if (!ostr) 340 goto alloc; 341 342 p1 = ostr; 343 while (p1 < ostr + strlen(ostr)) { 344 p2 = strchr(p1, ','); 345 if (!p2) 346 break; 347 348 p1 = p2 + 1; 349 i++; 350 } 351 352 alloc: 353 *size = i; 354 ptime = calloc(i, sizeof(*ptime)); 355 return ptime; 356 } 357 358 bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp) 359 { 360 /* if time is not set don't drop sample */ 361 if (timestamp == 0) 362 return false; 363 364 /* otherwise compare sample time to time window */ 365 if ((ptime->start && timestamp < ptime->start) || 366 (ptime->end && timestamp > ptime->end)) { 367 return true; 368 } 369 370 return false; 371 } 372 373 bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, 374 int num, u64 timestamp) 375 { 376 struct perf_time_interval *ptime; 377 int i; 378 379 if ((!ptime_buf) || (timestamp == 0) || (num == 0)) 380 return false; 381 382 if (num == 1) 383 return perf_time__skip_sample(&ptime_buf[0], timestamp); 384 385 /* 386 * start/end of multiple time ranges must be valid. 387 */ 388 for (i = 0; i < num; i++) { 389 ptime = &ptime_buf[i]; 390 391 if (timestamp >= ptime->start && 392 ((timestamp < ptime->end && i < num - 1) || 393 (timestamp <= ptime->end && i == num - 1))) { 394 break; 395 } 396 } 397 398 return (i == num) ? true : false; 399 } 400 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) 405 { 406 struct perf_time_interval *ptime_range; 407 int size, num, ret; 408 409 ptime_range = perf_time__range_alloc(time_str, &size); 410 if (!ptime_range) 411 return -ENOMEM; 412 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"); 419 ret = -EINVAL; 420 goto error; 421 } 422 423 num = perf_time__percent_parse_str( 424 ptime_range, size, 425 time_str, 426 session->evlist->first_sample_time, 427 session->evlist->last_sample_time); 428 429 if (num < 0) { 430 pr_err("Invalid time string\n"); 431 ret = -EINVAL; 432 goto error; 433 } 434 } else { 435 num = 1; 436 } 437 438 *range_size = size; 439 *range_num = num; 440 *ranges = ptime_range; 441 return 0; 442 443 error: 444 free(ptime_range); 445 return ret; 446 } 447 448 int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) 449 { 450 u64 sec = timestamp / NSEC_PER_SEC; 451 u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC; 452 453 return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); 454 } 455 456 int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz) 457 { 458 u64 sec = timestamp / NSEC_PER_SEC, 459 nsec = timestamp % NSEC_PER_SEC; 460 461 return scnprintf(buf, sz, "%" PRIu64 ".%09" PRIu64, sec, nsec); 462 } 463 464 int fetch_current_timestamp(char *buf, size_t sz) 465 { 466 struct timeval tv; 467 struct tm tm; 468 char dt[32]; 469 470 if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm)) 471 return -1; 472 473 if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm)) 474 return -1; 475 476 scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000); 477 478 return 0; 479 } 480