162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <subcmd/parse-options.h>
462306a36Sopenharmony_ci#include <stdio.h>
562306a36Sopenharmony_ci#include <time.h>
662306a36Sopenharmony_ci#include <strings.h>
762306a36Sopenharmony_ci#include <linux/time64.h>
862306a36Sopenharmony_ci#include "debug.h"
962306a36Sopenharmony_ci#include "clockid.h"
1062306a36Sopenharmony_ci#include "record.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistruct clockid_map {
1362306a36Sopenharmony_ci	const char *name;
1462306a36Sopenharmony_ci	int clockid;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define CLOCKID_MAP(n, c)	\
1862306a36Sopenharmony_ci	{ .name = n, .clockid = (c), }
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define CLOCKID_END	{ .name = NULL, }
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * Add the missing ones, we need to build on many distros...
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci#ifndef CLOCK_MONOTONIC_RAW
2762306a36Sopenharmony_ci#define CLOCK_MONOTONIC_RAW 4
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_ci#ifndef CLOCK_BOOTTIME
3062306a36Sopenharmony_ci#define CLOCK_BOOTTIME 7
3162306a36Sopenharmony_ci#endif
3262306a36Sopenharmony_ci#ifndef CLOCK_TAI
3362306a36Sopenharmony_ci#define CLOCK_TAI 11
3462306a36Sopenharmony_ci#endif
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const struct clockid_map clockids[] = {
3762306a36Sopenharmony_ci	/* available for all events, NMI safe */
3862306a36Sopenharmony_ci	CLOCKID_MAP("monotonic", CLOCK_MONOTONIC),
3962306a36Sopenharmony_ci	CLOCKID_MAP("monotonic_raw", CLOCK_MONOTONIC_RAW),
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* available for some events */
4262306a36Sopenharmony_ci	CLOCKID_MAP("realtime", CLOCK_REALTIME),
4362306a36Sopenharmony_ci	CLOCKID_MAP("boottime", CLOCK_BOOTTIME),
4462306a36Sopenharmony_ci	CLOCKID_MAP("tai", CLOCK_TAI),
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* available for the lazy */
4762306a36Sopenharmony_ci	CLOCKID_MAP("mono", CLOCK_MONOTONIC),
4862306a36Sopenharmony_ci	CLOCKID_MAP("raw", CLOCK_MONOTONIC_RAW),
4962306a36Sopenharmony_ci	CLOCKID_MAP("real", CLOCK_REALTIME),
5062306a36Sopenharmony_ci	CLOCKID_MAP("boot", CLOCK_BOOTTIME),
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	CLOCKID_END,
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int get_clockid_res(clockid_t clk_id, u64 *res_ns)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct timespec res;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	*res_ns = 0;
6062306a36Sopenharmony_ci	if (!clock_getres(clk_id, &res))
6162306a36Sopenharmony_ci		*res_ns = res.tv_nsec + res.tv_sec * NSEC_PER_SEC;
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		pr_warning("WARNING: Failed to determine specified clock resolution.\n");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint parse_clockid(const struct option *opt, const char *str, int unset)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct record_opts *opts = (struct record_opts *)opt->value;
7162306a36Sopenharmony_ci	const struct clockid_map *cm;
7262306a36Sopenharmony_ci	const char *ostr = str;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (unset) {
7562306a36Sopenharmony_ci		opts->use_clockid = 0;
7662306a36Sopenharmony_ci		return 0;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* no arg passed */
8062306a36Sopenharmony_ci	if (!str)
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* no setting it twice */
8462306a36Sopenharmony_ci	if (opts->use_clockid)
8562306a36Sopenharmony_ci		return -1;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	opts->use_clockid = true;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* if its a number, we're done */
9062306a36Sopenharmony_ci	if (sscanf(str, "%d", &opts->clockid) == 1)
9162306a36Sopenharmony_ci		return get_clockid_res(opts->clockid, &opts->clockid_res_ns);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* allow a "CLOCK_" prefix to the name */
9462306a36Sopenharmony_ci	if (!strncasecmp(str, "CLOCK_", 6))
9562306a36Sopenharmony_ci		str += 6;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for (cm = clockids; cm->name; cm++) {
9862306a36Sopenharmony_ci		if (!strcasecmp(str, cm->name)) {
9962306a36Sopenharmony_ci			opts->clockid = cm->clockid;
10062306a36Sopenharmony_ci			return get_clockid_res(opts->clockid,
10162306a36Sopenharmony_ci					       &opts->clockid_res_ns);
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	opts->use_clockid = false;
10662306a36Sopenharmony_ci	ui__warning("unknown clockid %s, check man page\n", ostr);
10762306a36Sopenharmony_ci	return -1;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciconst char *clockid_name(clockid_t clk_id)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	const struct clockid_map *cm;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	for (cm = clockids; cm->name; cm++) {
11562306a36Sopenharmony_ci		if (cm->clockid == clk_id)
11662306a36Sopenharmony_ci			return cm->name;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	return "(not found)";
11962306a36Sopenharmony_ci}
120