brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 1b91ccd Raw
560 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <stdlib.h>3#include <string.h>4#include <linux/string.h>5#include <sys/time.h>6#include <linux/time64.h>7#include <time.h>8#include <errno.h>9#include <inttypes.h>10#include <math.h>11#include <linux/ctype.h>12 13#include "debug.h"14#include "time-utils.h"15#include "session.h"16#include "evlist.h"17 18int parse_nsec_time(const char *str, u64 *ptime)19{20	u64 time_sec, time_nsec;21	char *end;22 23	time_sec = strtoull(str, &end, 10);24	if (*end != '.' && *end != '\0')25		return -1;26 27	if (*end == '.') {28		int i;29		char nsec_buf[10];30 31		if (strlen(++end) > 9)32			return -1;33 34		strncpy(nsec_buf, end, 9);35		nsec_buf[9] = '\0';36 37		/* make it nsec precision */38		for (i = strlen(nsec_buf); i < 9; i++)39			nsec_buf[i] = '0';40 41		time_nsec = strtoull(nsec_buf, &end, 10);42		if (*end != '\0')43			return -1;44	} else45		time_nsec = 0;46 47	*ptime = time_sec * NSEC_PER_SEC + time_nsec;48	return 0;49}50 51static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,52				  char *start_str, char *end_str)53{54	if (start_str && (*start_str != '\0') &&55	    (parse_nsec_time(start_str, &ptime->start) != 0)) {56		return -1;57	}58 59	if (end_str && (*end_str != '\0') &&60	    (parse_nsec_time(end_str, &ptime->end) != 0)) {61		return -1;62	}63 64	return 0;65}66 67static int split_start_end(char **start, char **end, const char *ostr, char ch)68{69	char *start_str, *end_str;70	char *d, *str;71 72	if (ostr == NULL || *ostr == '\0')73		return 0;74 75	/* copy original string because we need to modify it */76	str = strdup(ostr);77	if (str == NULL)78		return -ENOMEM;79 80	start_str = str;81	d = strchr(start_str, ch);82	if (d) {83		*d = '\0';84		++d;85	}86	end_str = d;87 88	*start = start_str;89	*end = end_str;90 91	return 0;92}93 94int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)95{96	char *start_str = NULL, *end_str;97	int rc;98 99	rc = split_start_end(&start_str, &end_str, ostr, ',');100	if (rc || !start_str)101		return rc;102 103	ptime->start = 0;104	ptime->end = 0;105 106	rc = parse_timestr_sec_nsec(ptime, start_str, end_str);107 108	free(start_str);109 110	/* make sure end time is after start time if it was given */111	if (rc == 0 && ptime->end && ptime->end < ptime->start)112		return -EINVAL;113 114	pr_debug("start time %" PRIu64 ", ", ptime->start);115	pr_debug("end time %" PRIu64 "\n", ptime->end);116 117	return rc;118}119 120static int perf_time__parse_strs(struct perf_time_interval *ptime,121				 const char *ostr, int size)122{123	const char *cp;124	char *str, *arg, *p;125	int i, num = 0, rc = 0;126 127	/* Count the commas */128	for (cp = ostr; *cp; cp++)129		num += !!(*cp == ',');130 131	if (!num)132		return -EINVAL;133 134	BUG_ON(num > size);135 136	str = strdup(ostr);137	if (!str)138		return -ENOMEM;139 140	/* Split the string and parse each piece, except the last */141	for (i = 0, p = str; i < num - 1; i++) {142		arg = p;143		/* Find next comma, there must be one */144		p = skip_spaces(strchr(p, ',') + 1);145		/* Skip the value, must not contain space or comma */146		while (*p && !isspace(*p)) {147			if (*p++ == ',') {148				rc = -EINVAL;149				goto out;150			}151		}152		/* Split and parse */153		if (*p)154			*p++ = 0;155		rc = perf_time__parse_str(ptime + i, arg);156		if (rc < 0)157			goto out;158	}159 160	/* Parse the last piece */161	rc = perf_time__parse_str(ptime + i, p);162	if (rc < 0)163		goto out;164 165	/* Check there is no overlap */166	for (i = 0; i < num - 1; i++) {167		if (ptime[i].end >= ptime[i + 1].start) {168			rc = -EINVAL;169			goto out;170		}171	}172 173	rc = num;174out:175	free(str);176 177	return rc;178}179 180static int parse_percent(double *pcnt, char *str)181{182	char *c, *endptr;183	double d;184 185	c = strchr(str, '%');186	if (c)187		*c = '\0';188	else189		return -1;190 191	d = strtod(str, &endptr);192	if (endptr != str + strlen(str))193		return -1;194 195	*pcnt = d / 100.0;196	return 0;197}198 199static int set_percent_time(struct perf_time_interval *ptime, double start_pcnt,200			    double end_pcnt, u64 start, u64 end)201{202	u64 total = end - start;203 204	if (start_pcnt < 0.0 || start_pcnt > 1.0 ||205	    end_pcnt < 0.0 || end_pcnt > 1.0) {206		return -1;207	}208 209	ptime->start = start + round(start_pcnt * total);210	ptime->end = start + round(end_pcnt * total);211 212	if (ptime->end > ptime->start && ptime->end != end)213		ptime->end -= 1;214 215	return 0;216}217 218static int percent_slash_split(char *str, struct perf_time_interval *ptime,219			       u64 start, u64 end)220{221	char *p, *end_str;222	double pcnt, start_pcnt, end_pcnt;223	int i;224 225	/*226	 * Example:227	 * 10%/2: select the second 10% slice and the third 10% slice228	 */229 230	/* We can modify this string since the original one is copied */231	p = strchr(str, '/');232	if (!p)233		return -1;234 235	*p = '\0';236	if (parse_percent(&pcnt, str) < 0)237		return -1;238 239	p++;240	i = (int)strtol(p, &end_str, 10);241	if (*end_str)242		return -1;243 244	if (pcnt <= 0.0)245		return -1;246 247	start_pcnt = pcnt * (i - 1);248	end_pcnt = pcnt * i;249 250	return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);251}252 253static int percent_dash_split(char *str, struct perf_time_interval *ptime,254			      u64 start, u64 end)255{256	char *start_str = NULL, *end_str;257	double start_pcnt, end_pcnt;258	int ret;259 260	/*261	 * Example: 0%-10%262	 */263 264	ret = split_start_end(&start_str, &end_str, str, '-');265	if (ret || !start_str)266		return ret;267 268	if ((parse_percent(&start_pcnt, start_str) != 0) ||269	    (parse_percent(&end_pcnt, end_str) != 0)) {270		free(start_str);271		return -1;272	}273 274	free(start_str);275 276	return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);277}278 279typedef int (*time_pecent_split)(char *, struct perf_time_interval *,280				 u64 start, u64 end);281 282static int percent_comma_split(struct perf_time_interval *ptime_buf, int num,283			       const char *ostr, u64 start, u64 end,284			       time_pecent_split func)285{286	char *str, *p1, *p2;287	int len, ret, i = 0;288 289	str = strdup(ostr);290	if (str == NULL)291		return -ENOMEM;292 293	len = strlen(str);294	p1 = str;295 296	while (p1 < str + len) {297		if (i >= num) {298			free(str);299			return -1;300		}301 302		p2 = strchr(p1, ',');303		if (p2)304			*p2 = '\0';305 306		ret = (func)(p1, &ptime_buf[i], start, end);307		if (ret < 0) {308			free(str);309			return -1;310		}311 312		pr_debug("start time %d: %" PRIu64 ", ", i, ptime_buf[i].start);313		pr_debug("end time %d: %" PRIu64 "\n", i, ptime_buf[i].end);314 315		i++;316 317		if (p2)318			p1 = p2 + 1;319		else320			break;321	}322 323	free(str);324	return i;325}326 327static int one_percent_convert(struct perf_time_interval *ptime_buf,328			       const char *ostr, u64 start, u64 end, char *c)329{330	char *str;331	int len = strlen(ostr), ret;332 333	/*334	 * c points to '%'.335	 * '%' should be the last character336	 */337	if (ostr + len - 1 != c)338		return -1;339 340	/*341	 * Construct a string like "xx%/1"342	 */343	str = malloc(len + 3);344	if (str == NULL)345		return -ENOMEM;346 347	memcpy(str, ostr, len);348	strcpy(str + len, "/1");349 350	ret = percent_slash_split(str, ptime_buf, start, end);351	if (ret == 0)352		ret = 1;353 354	free(str);355	return ret;356}357 358int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,359				 const char *ostr, u64 start, u64 end)360{361	char *c;362 363	/*364	 * ostr example:365	 * 10%/2,10%/3: select the second 10% slice and the third 10% slice366	 * 0%-10%,30%-40%: multiple time range367	 * 50%: just one percent368	 */369 370	memset(ptime_buf, 0, sizeof(*ptime_buf) * num);371 372	c = strchr(ostr, '/');373	if (c) {374		return percent_comma_split(ptime_buf, num, ostr, start,375					   end, percent_slash_split);376	}377 378	c = strchr(ostr, '-');379	if (c) {380		return percent_comma_split(ptime_buf, num, ostr, start,381					   end, percent_dash_split);382	}383 384	c = strchr(ostr, '%');385	if (c)386		return one_percent_convert(ptime_buf, ostr, start, end, c);387 388	return -1;389}390 391struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size)392{393	const char *p1, *p2;394	int i = 1;395	struct perf_time_interval *ptime;396 397	/*398	 * At least allocate one time range.399	 */400	if (!ostr)401		goto alloc;402 403	p1 = ostr;404	while (p1 < ostr + strlen(ostr)) {405		p2 = strchr(p1, ',');406		if (!p2)407			break;408 409		p1 = p2 + 1;410		i++;411	}412 413alloc:414	*size = i;415	ptime = calloc(i, sizeof(*ptime));416	return ptime;417}418 419bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)420{421	/* if time is not set don't drop sample */422	if (timestamp == 0)423		return false;424 425	/* otherwise compare sample time to time window */426	if ((ptime->start && timestamp < ptime->start) ||427	    (ptime->end && timestamp > ptime->end)) {428		return true;429	}430 431	return false;432}433 434bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,435				   int num, u64 timestamp)436{437	struct perf_time_interval *ptime;438	int i;439 440	if ((!ptime_buf) || (timestamp == 0) || (num == 0))441		return false;442 443	if (num == 1)444		return perf_time__skip_sample(&ptime_buf[0], timestamp);445 446	/*447	 * start/end of multiple time ranges must be valid.448	 */449	for (i = 0; i < num; i++) {450		ptime = &ptime_buf[i];451 452		if (timestamp >= ptime->start &&453		    (timestamp <= ptime->end || !ptime->end)) {454			return false;455		}456	}457 458	return true;459}460 461int perf_time__parse_for_ranges_reltime(const char *time_str,462				struct perf_session *session,463				struct perf_time_interval **ranges,464				int *range_size, int *range_num,465				bool reltime)466{467	bool has_percent = strchr(time_str, '%');468	struct perf_time_interval *ptime_range;469	int size, num, ret = -EINVAL;470 471	ptime_range = perf_time__range_alloc(time_str, &size);472	if (!ptime_range)473		return -ENOMEM;474 475	if (has_percent || reltime) {476		if (session->evlist->first_sample_time == 0 &&477		    session->evlist->last_sample_time == 0) {478			pr_err("HINT: no first/last sample time found in perf data.\n"479			       "Please use latest perf binary to execute 'perf record'\n"480			       "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");481			goto error;482		}483	}484 485	if (has_percent) {486		num = perf_time__percent_parse_str(487				ptime_range, size,488				time_str,489				session->evlist->first_sample_time,490				session->evlist->last_sample_time);491	} else {492		num = perf_time__parse_strs(ptime_range, time_str, size);493	}494 495	if (num < 0)496		goto error_invalid;497 498	if (reltime) {499		int i;500 501		for (i = 0; i < num; i++) {502			ptime_range[i].start += session->evlist->first_sample_time;503			ptime_range[i].end += session->evlist->first_sample_time;504		}505	}506 507	*range_size = size;508	*range_num = num;509	*ranges = ptime_range;510	return 0;511 512error_invalid:513	pr_err("Invalid time string\n");514error:515	free(ptime_range);516	return ret;517}518 519int perf_time__parse_for_ranges(const char *time_str,520				struct perf_session *session,521				struct perf_time_interval **ranges,522				int *range_size, int *range_num)523{524	return perf_time__parse_for_ranges_reltime(time_str, session, ranges,525					range_size, range_num, false);526}527 528int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)529{530	u64  sec = timestamp / NSEC_PER_SEC;531	u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC;532 533	return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec);534}535 536int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz)537{538	u64 sec  = timestamp / NSEC_PER_SEC,539	    nsec = timestamp % NSEC_PER_SEC;540 541	return scnprintf(buf, sz, "%" PRIu64 ".%09" PRIu64, sec, nsec);542}543 544int fetch_current_timestamp(char *buf, size_t sz)545{546	struct timeval tv;547	struct tm tm;548	char dt[32];549 550	if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm))551		return -1;552 553	if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm))554		return -1;555 556	scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000);557 558	return 0;559}560