Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdlib.h>
0003 #include <string.h>
0004 #include <linux/string.h>
0005 #include <sys/time.h>
0006 #include <linux/time64.h>
0007 #include <time.h>
0008 #include <errno.h>
0009 #include <inttypes.h>
0010 #include <math.h>
0011 #include <linux/ctype.h>
0012 
0013 #include "debug.h"
0014 #include "time-utils.h"
0015 #include "session.h"
0016 #include "evlist.h"
0017 
0018 int parse_nsec_time(const char *str, u64 *ptime)
0019 {
0020     u64 time_sec, time_nsec;
0021     char *end;
0022 
0023     time_sec = strtoul(str, &end, 10);
0024     if (*end != '.' && *end != '\0')
0025         return -1;
0026 
0027     if (*end == '.') {
0028         int i;
0029         char nsec_buf[10];
0030 
0031         if (strlen(++end) > 9)
0032             return -1;
0033 
0034         strncpy(nsec_buf, end, 9);
0035         nsec_buf[9] = '\0';
0036 
0037         /* make it nsec precision */
0038         for (i = strlen(nsec_buf); i < 9; i++)
0039             nsec_buf[i] = '0';
0040 
0041         time_nsec = strtoul(nsec_buf, &end, 10);
0042         if (*end != '\0')
0043             return -1;
0044     } else
0045         time_nsec = 0;
0046 
0047     *ptime = time_sec * NSEC_PER_SEC + time_nsec;
0048     return 0;
0049 }
0050 
0051 static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,
0052                   char *start_str, char *end_str)
0053 {
0054     if (start_str && (*start_str != '\0') &&
0055         (parse_nsec_time(start_str, &ptime->start) != 0)) {
0056         return -1;
0057     }
0058 
0059     if (end_str && (*end_str != '\0') &&
0060         (parse_nsec_time(end_str, &ptime->end) != 0)) {
0061         return -1;
0062     }
0063 
0064     return 0;
0065 }
0066 
0067 static int split_start_end(char **start, char **end, const char *ostr, char ch)
0068 {
0069     char *start_str, *end_str;
0070     char *d, *str;
0071 
0072     if (ostr == NULL || *ostr == '\0')
0073         return 0;
0074 
0075     /* copy original string because we need to modify it */
0076     str = strdup(ostr);
0077     if (str == NULL)
0078         return -ENOMEM;
0079 
0080     start_str = str;
0081     d = strchr(start_str, ch);
0082     if (d) {
0083         *d = '\0';
0084         ++d;
0085     }
0086     end_str = d;
0087 
0088     *start = start_str;
0089     *end = end_str;
0090 
0091     return 0;
0092 }
0093 
0094 int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
0095 {
0096     char *start_str = NULL, *end_str;
0097     int rc;
0098 
0099     rc = split_start_end(&start_str, &end_str, ostr, ',');
0100     if (rc || !start_str)
0101         return rc;
0102 
0103     ptime->start = 0;
0104     ptime->end = 0;
0105 
0106     rc = parse_timestr_sec_nsec(ptime, start_str, end_str);
0107 
0108     free(start_str);
0109 
0110     /* make sure end time is after start time if it was given */
0111     if (rc == 0 && ptime->end && ptime->end < ptime->start)
0112         return -EINVAL;
0113 
0114     pr_debug("start time %" PRIu64 ", ", ptime->start);
0115     pr_debug("end time %" PRIu64 "\n", ptime->end);
0116 
0117     return rc;
0118 }
0119 
0120 static int perf_time__parse_strs(struct perf_time_interval *ptime,
0121                  const char *ostr, int size)
0122 {
0123     const char *cp;
0124     char *str, *arg, *p;
0125     int i, num = 0, rc = 0;
0126 
0127     /* Count the commas */
0128     for (cp = ostr; *cp; cp++)
0129         num += !!(*cp == ',');
0130 
0131     if (!num)
0132         return -EINVAL;
0133 
0134     BUG_ON(num > size);
0135 
0136     str = strdup(ostr);
0137     if (!str)
0138         return -ENOMEM;
0139 
0140     /* Split the string and parse each piece, except the last */
0141     for (i = 0, p = str; i < num - 1; i++) {
0142         arg = p;
0143         /* Find next comma, there must be one */
0144         p = skip_spaces(strchr(p, ',') + 1);
0145         /* Skip the value, must not contain space or comma */
0146         while (*p && !isspace(*p)) {
0147             if (*p++ == ',') {
0148                 rc = -EINVAL;
0149                 goto out;
0150             }
0151         }
0152         /* Split and parse */
0153         if (*p)
0154             *p++ = 0;
0155         rc = perf_time__parse_str(ptime + i, arg);
0156         if (rc < 0)
0157             goto out;
0158     }
0159 
0160     /* Parse the last piece */
0161     rc = perf_time__parse_str(ptime + i, p);
0162     if (rc < 0)
0163         goto out;
0164 
0165     /* Check there is no overlap */
0166     for (i = 0; i < num - 1; i++) {
0167         if (ptime[i].end >= ptime[i + 1].start) {
0168             rc = -EINVAL;
0169             goto out;
0170         }
0171     }
0172 
0173     rc = num;
0174 out:
0175     free(str);
0176 
0177     return rc;
0178 }
0179 
0180 static int parse_percent(double *pcnt, char *str)
0181 {
0182     char *c, *endptr;
0183     double d;
0184 
0185     c = strchr(str, '%');
0186     if (c)
0187         *c = '\0';
0188     else
0189         return -1;
0190 
0191     d = strtod(str, &endptr);
0192     if (endptr != str + strlen(str))
0193         return -1;
0194 
0195     *pcnt = d / 100.0;
0196     return 0;
0197 }
0198 
0199 static int set_percent_time(struct perf_time_interval *ptime, double start_pcnt,
0200                 double end_pcnt, u64 start, u64 end)
0201 {
0202     u64 total = end - start;
0203 
0204     if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
0205         end_pcnt < 0.0 || end_pcnt > 1.0) {
0206         return -1;
0207     }
0208 
0209     ptime->start = start + round(start_pcnt * total);
0210     ptime->end = start + round(end_pcnt * total);
0211 
0212     if (ptime->end > ptime->start && ptime->end != end)
0213         ptime->end -= 1;
0214 
0215     return 0;
0216 }
0217 
0218 static int percent_slash_split(char *str, struct perf_time_interval *ptime,
0219                    u64 start, u64 end)
0220 {
0221     char *p, *end_str;
0222     double pcnt, start_pcnt, end_pcnt;
0223     int i;
0224 
0225     /*
0226      * Example:
0227      * 10%/2: select the second 10% slice and the third 10% slice
0228      */
0229 
0230     /* We can modify this string since the original one is copied */
0231     p = strchr(str, '/');
0232     if (!p)
0233         return -1;
0234 
0235     *p = '\0';
0236     if (parse_percent(&pcnt, str) < 0)
0237         return -1;
0238 
0239     p++;
0240     i = (int)strtol(p, &end_str, 10);
0241     if (*end_str)
0242         return -1;
0243 
0244     if (pcnt <= 0.0)
0245         return -1;
0246 
0247     start_pcnt = pcnt * (i - 1);
0248     end_pcnt = pcnt * i;
0249 
0250     return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);
0251 }
0252 
0253 static int percent_dash_split(char *str, struct perf_time_interval *ptime,
0254                   u64 start, u64 end)
0255 {
0256     char *start_str = NULL, *end_str;
0257     double start_pcnt, end_pcnt;
0258     int ret;
0259 
0260     /*
0261      * Example: 0%-10%
0262      */
0263 
0264     ret = split_start_end(&start_str, &end_str, str, '-');
0265     if (ret || !start_str)
0266         return ret;
0267 
0268     if ((parse_percent(&start_pcnt, start_str) != 0) ||
0269         (parse_percent(&end_pcnt, end_str) != 0)) {
0270         free(start_str);
0271         return -1;
0272     }
0273 
0274     free(start_str);
0275 
0276     return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);
0277 }
0278 
0279 typedef int (*time_pecent_split)(char *, struct perf_time_interval *,
0280                  u64 start, u64 end);
0281 
0282 static int percent_comma_split(struct perf_time_interval *ptime_buf, int num,
0283                    const char *ostr, u64 start, u64 end,
0284                    time_pecent_split func)
0285 {
0286     char *str, *p1, *p2;
0287     int len, ret, i = 0;
0288 
0289     str = strdup(ostr);
0290     if (str == NULL)
0291         return -ENOMEM;
0292 
0293     len = strlen(str);
0294     p1 = str;
0295 
0296     while (p1 < str + len) {
0297         if (i >= num) {
0298             free(str);
0299             return -1;
0300         }
0301 
0302         p2 = strchr(p1, ',');
0303         if (p2)
0304             *p2 = '\0';
0305 
0306         ret = (func)(p1, &ptime_buf[i], start, end);
0307         if (ret < 0) {
0308             free(str);
0309             return -1;
0310         }
0311 
0312         pr_debug("start time %d: %" PRIu64 ", ", i, ptime_buf[i].start);
0313         pr_debug("end time %d: %" PRIu64 "\n", i, ptime_buf[i].end);
0314 
0315         i++;
0316 
0317         if (p2)
0318             p1 = p2 + 1;
0319         else
0320             break;
0321     }
0322 
0323     free(str);
0324     return i;
0325 }
0326 
0327 static int one_percent_convert(struct perf_time_interval *ptime_buf,
0328                    const char *ostr, u64 start, u64 end, char *c)
0329 {
0330     char *str;
0331     int len = strlen(ostr), ret;
0332 
0333     /*
0334      * c points to '%'.
0335      * '%' should be the last character
0336      */
0337     if (ostr + len - 1 != c)
0338         return -1;
0339 
0340     /*
0341      * Construct a string like "xx%/1"
0342      */
0343     str = malloc(len + 3);
0344     if (str == NULL)
0345         return -ENOMEM;
0346 
0347     memcpy(str, ostr, len);
0348     strcpy(str + len, "/1");
0349 
0350     ret = percent_slash_split(str, ptime_buf, start, end);
0351     if (ret == 0)
0352         ret = 1;
0353 
0354     free(str);
0355     return ret;
0356 }
0357 
0358 int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,
0359                  const char *ostr, u64 start, u64 end)
0360 {
0361     char *c;
0362 
0363     /*
0364      * ostr example:
0365      * 10%/2,10%/3: select the second 10% slice and the third 10% slice
0366      * 0%-10%,30%-40%: multiple time range
0367      * 50%: just one percent
0368      */
0369 
0370     memset(ptime_buf, 0, sizeof(*ptime_buf) * num);
0371 
0372     c = strchr(ostr, '/');
0373     if (c) {
0374         return percent_comma_split(ptime_buf, num, ostr, start,
0375                        end, percent_slash_split);
0376     }
0377 
0378     c = strchr(ostr, '-');
0379     if (c) {
0380         return percent_comma_split(ptime_buf, num, ostr, start,
0381                        end, percent_dash_split);
0382     }
0383 
0384     c = strchr(ostr, '%');
0385     if (c)
0386         return one_percent_convert(ptime_buf, ostr, start, end, c);
0387 
0388     return -1;
0389 }
0390 
0391 struct perf_time_interval *perf_time__range_alloc(const char *ostr, int *size)
0392 {
0393     const char *p1, *p2;
0394     int i = 1;
0395     struct perf_time_interval *ptime;
0396 
0397     /*
0398      * At least allocate one time range.
0399      */
0400     if (!ostr)
0401         goto alloc;
0402 
0403     p1 = ostr;
0404     while (p1 < ostr + strlen(ostr)) {
0405         p2 = strchr(p1, ',');
0406         if (!p2)
0407             break;
0408 
0409         p1 = p2 + 1;
0410         i++;
0411     }
0412 
0413 alloc:
0414     *size = i;
0415     ptime = calloc(i, sizeof(*ptime));
0416     return ptime;
0417 }
0418 
0419 bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
0420 {
0421     /* if time is not set don't drop sample */
0422     if (timestamp == 0)
0423         return false;
0424 
0425     /* otherwise compare sample time to time window */
0426     if ((ptime->start && timestamp < ptime->start) ||
0427         (ptime->end && timestamp > ptime->end)) {
0428         return true;
0429     }
0430 
0431     return false;
0432 }
0433 
0434 bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
0435                    int num, u64 timestamp)
0436 {
0437     struct perf_time_interval *ptime;
0438     int i;
0439 
0440     if ((!ptime_buf) || (timestamp == 0) || (num == 0))
0441         return false;
0442 
0443     if (num == 1)
0444         return perf_time__skip_sample(&ptime_buf[0], timestamp);
0445 
0446     /*
0447      * start/end of multiple time ranges must be valid.
0448      */
0449     for (i = 0; i < num; i++) {
0450         ptime = &ptime_buf[i];
0451 
0452         if (timestamp >= ptime->start &&
0453             (timestamp <= ptime->end || !ptime->end)) {
0454             return false;
0455         }
0456     }
0457 
0458     return true;
0459 }
0460 
0461 int perf_time__parse_for_ranges_reltime(const char *time_str,
0462                 struct perf_session *session,
0463                 struct perf_time_interval **ranges,
0464                 int *range_size, int *range_num,
0465                 bool reltime)
0466 {
0467     bool has_percent = strchr(time_str, '%');
0468     struct perf_time_interval *ptime_range;
0469     int size, num, ret = -EINVAL;
0470 
0471     ptime_range = perf_time__range_alloc(time_str, &size);
0472     if (!ptime_range)
0473         return -ENOMEM;
0474 
0475     if (has_percent || reltime) {
0476         if (session->evlist->first_sample_time == 0 &&
0477             session->evlist->last_sample_time == 0) {
0478             pr_err("HINT: no first/last sample time found in perf data.\n"
0479                    "Please use latest perf binary to execute 'perf record'\n"
0480                    "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
0481             goto error;
0482         }
0483     }
0484 
0485     if (has_percent) {
0486         num = perf_time__percent_parse_str(
0487                 ptime_range, size,
0488                 time_str,
0489                 session->evlist->first_sample_time,
0490                 session->evlist->last_sample_time);
0491     } else {
0492         num = perf_time__parse_strs(ptime_range, time_str, size);
0493     }
0494 
0495     if (num < 0)
0496         goto error_invalid;
0497 
0498     if (reltime) {
0499         int i;
0500 
0501         for (i = 0; i < num; i++) {
0502             ptime_range[i].start += session->evlist->first_sample_time;
0503             ptime_range[i].end += session->evlist->first_sample_time;
0504         }
0505     }
0506 
0507     *range_size = size;
0508     *range_num = num;
0509     *ranges = ptime_range;
0510     return 0;
0511 
0512 error_invalid:
0513     pr_err("Invalid time string\n");
0514 error:
0515     free(ptime_range);
0516     return ret;
0517 }
0518 
0519 int perf_time__parse_for_ranges(const char *time_str,
0520                 struct perf_session *session,
0521                 struct perf_time_interval **ranges,
0522                 int *range_size, int *range_num)
0523 {
0524     return perf_time__parse_for_ranges_reltime(time_str, session, ranges,
0525                     range_size, range_num, false);
0526 }
0527 
0528 int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
0529 {
0530     u64  sec = timestamp / NSEC_PER_SEC;
0531     u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC;
0532 
0533     return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec);
0534 }
0535 
0536 int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz)
0537 {
0538     u64 sec  = timestamp / NSEC_PER_SEC,
0539         nsec = timestamp % NSEC_PER_SEC;
0540 
0541     return scnprintf(buf, sz, "%" PRIu64 ".%09" PRIu64, sec, nsec);
0542 }
0543 
0544 int fetch_current_timestamp(char *buf, size_t sz)
0545 {
0546     struct timeval tv;
0547     struct tm tm;
0548     char dt[32];
0549 
0550     if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm))
0551         return -1;
0552 
0553     if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm))
0554         return -1;
0555 
0556     scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000);
0557 
0558     return 0;
0559 }