cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

evlist-open-close.c (6768B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <inttypes.h>
      3#include <stdio.h>
      4#include <stdlib.h>
      5#include <limits.h>
      6#include "bench.h"
      7#include "../util/debug.h"
      8#include "../util/stat.h"
      9#include "../util/evlist.h"
     10#include "../util/evsel.h"
     11#include "../util/strbuf.h"
     12#include "../util/record.h"
     13#include "../util/parse-events.h"
     14#include "internal/threadmap.h"
     15#include "internal/cpumap.h"
     16#include <linux/perf_event.h>
     17#include <linux/kernel.h>
     18#include <linux/time64.h>
     19#include <linux/string.h>
     20#include <subcmd/parse-options.h>
     21
     22#define MMAP_FLUSH_DEFAULT 1
     23
     24static int iterations = 100;
     25static int nr_events = 1;
     26static const char *event_string = "dummy";
     27
     28static inline u64 timeval2usec(struct timeval *tv)
     29{
     30	return tv->tv_sec * USEC_PER_SEC + tv->tv_usec;
     31}
     32
     33static struct record_opts opts = {
     34	.sample_time	     = true,
     35	.mmap_pages	     = UINT_MAX,
     36	.user_freq	     = UINT_MAX,
     37	.user_interval	     = ULLONG_MAX,
     38	.freq		     = 4000,
     39	.target		     = {
     40		.uses_mmap   = true,
     41		.default_per_cpu = true,
     42	},
     43	.mmap_flush          = MMAP_FLUSH_DEFAULT,
     44	.nr_threads_synthesize = 1,
     45	.ctl_fd              = -1,
     46	.ctl_fd_ack          = -1,
     47};
     48
     49static const struct option options[] = {
     50	OPT_STRING('e', "event", &event_string, "event", "event selector. use 'perf list' to list available events"),
     51	OPT_INTEGER('n', "nr-events", &nr_events,
     52		     "number of dummy events to create (default 1). If used with -e, it clones those events n times (1 = no change)"),
     53	OPT_INTEGER('i', "iterations", &iterations, "Number of iterations used to compute average (default=100)"),
     54	OPT_BOOLEAN('a', "all-cpus", &opts.target.system_wide, "system-wide collection from all CPUs"),
     55	OPT_STRING('C', "cpu", &opts.target.cpu_list, "cpu", "list of cpus where to open events"),
     56	OPT_STRING('p', "pid", &opts.target.pid, "pid", "record events on existing process id"),
     57	OPT_STRING('t', "tid", &opts.target.tid, "tid", "record events on existing thread id"),
     58	OPT_STRING('u', "uid", &opts.target.uid_str, "user", "user to profile"),
     59	OPT_BOOLEAN(0, "per-thread", &opts.target.per_thread, "use per-thread mmaps"),
     60	OPT_END()
     61};
     62
     63static const char *const bench_usage[] = {
     64	"perf bench internals evlist-open-close <options>",
     65	NULL
     66};
     67
     68static int evlist__count_evsel_fds(struct evlist *evlist)
     69{
     70	struct evsel *evsel;
     71	int cnt = 0;
     72
     73	evlist__for_each_entry(evlist, evsel)
     74		cnt += evsel->core.threads->nr * perf_cpu_map__nr(evsel->core.cpus);
     75
     76	return cnt;
     77}
     78
     79static struct evlist *bench__create_evlist(char *evstr)
     80{
     81	struct parse_events_error err;
     82	struct evlist *evlist = evlist__new();
     83	int ret;
     84
     85	if (!evlist) {
     86		pr_err("Not enough memory to create evlist\n");
     87		return NULL;
     88	}
     89
     90	parse_events_error__init(&err);
     91	ret = parse_events(evlist, evstr, &err);
     92	if (ret) {
     93		parse_events_error__print(&err, evstr);
     94		parse_events_error__exit(&err);
     95		pr_err("Run 'perf list' for a list of valid events\n");
     96		ret = 1;
     97		goto out_delete_evlist;
     98	}
     99	parse_events_error__exit(&err);
    100	ret = evlist__create_maps(evlist, &opts.target);
    101	if (ret < 0) {
    102		pr_err("Not enough memory to create thread/cpu maps\n");
    103		goto out_delete_evlist;
    104	}
    105
    106	evlist__config(evlist, &opts, NULL);
    107
    108	return evlist;
    109
    110out_delete_evlist:
    111	evlist__delete(evlist);
    112	return NULL;
    113}
    114
    115static int bench__do_evlist_open_close(struct evlist *evlist)
    116{
    117	char sbuf[STRERR_BUFSIZE];
    118	int err = evlist__open(evlist);
    119
    120	if (err < 0) {
    121		pr_err("evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
    122		return err;
    123	}
    124
    125	err = evlist__mmap(evlist, opts.mmap_pages);
    126	if (err < 0) {
    127		pr_err("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
    128		return err;
    129	}
    130
    131	evlist__enable(evlist);
    132	evlist__disable(evlist);
    133	evlist__munmap(evlist);
    134	evlist__close(evlist);
    135
    136	return 0;
    137}
    138
    139static int bench_evlist_open_close__run(char *evstr)
    140{
    141	// used to print statistics only
    142	struct evlist *evlist = bench__create_evlist(evstr);
    143	double time_average, time_stddev;
    144	struct timeval start, end, diff;
    145	struct stats time_stats;
    146	u64 runtime_us;
    147	int i, err;
    148
    149	if (!evlist)
    150		return -ENOMEM;
    151
    152	init_stats(&time_stats);
    153
    154	printf("  Number of cpus:\t%d\n", perf_cpu_map__nr(evlist->core.user_requested_cpus));
    155	printf("  Number of threads:\t%d\n", evlist->core.threads->nr);
    156	printf("  Number of events:\t%d (%d fds)\n",
    157		evlist->core.nr_entries, evlist__count_evsel_fds(evlist));
    158	printf("  Number of iterations:\t%d\n", iterations);
    159
    160	evlist__delete(evlist);
    161
    162	for (i = 0; i < iterations; i++) {
    163		pr_debug("Started iteration %d\n", i);
    164		evlist = bench__create_evlist(evstr);
    165		if (!evlist)
    166			return -ENOMEM;
    167
    168		gettimeofday(&start, NULL);
    169		err = bench__do_evlist_open_close(evlist);
    170		if (err) {
    171			evlist__delete(evlist);
    172			return err;
    173		}
    174
    175		gettimeofday(&end, NULL);
    176		timersub(&end, &start, &diff);
    177		runtime_us = timeval2usec(&diff);
    178		update_stats(&time_stats, runtime_us);
    179
    180		evlist__delete(evlist);
    181		pr_debug("Iteration %d took:\t%" PRIu64 "us\n", i, runtime_us);
    182	}
    183
    184	time_average = avg_stats(&time_stats);
    185	time_stddev = stddev_stats(&time_stats);
    186	printf("  Average open-close took: %.3f usec (+- %.3f usec)\n", time_average, time_stddev);
    187
    188	return 0;
    189}
    190
    191static char *bench__repeat_event_string(const char *evstr, int n)
    192{
    193	char sbuf[STRERR_BUFSIZE];
    194	struct strbuf buf;
    195	int i, str_size = strlen(evstr),
    196	    final_size = str_size * n + n,
    197	    err = strbuf_init(&buf, final_size);
    198
    199	if (err) {
    200		pr_err("strbuf_init: %s\n", str_error_r(err, sbuf, sizeof(sbuf)));
    201		goto out_error;
    202	}
    203
    204	for (i = 0; i < n; i++) {
    205		err = strbuf_add(&buf, evstr, str_size);
    206		if (err) {
    207			pr_err("strbuf_add: %s\n", str_error_r(err, sbuf, sizeof(sbuf)));
    208			goto out_error;
    209		}
    210
    211		err = strbuf_addch(&buf, i == n-1 ? '\0' : ',');
    212		if (err) {
    213			pr_err("strbuf_addch: %s\n", str_error_r(err, sbuf, sizeof(sbuf)));
    214			goto out_error;
    215		}
    216	}
    217
    218	return strbuf_detach(&buf, NULL);
    219
    220out_error:
    221	strbuf_release(&buf);
    222	return NULL;
    223}
    224
    225
    226int bench_evlist_open_close(int argc, const char **argv)
    227{
    228	char *evstr, errbuf[BUFSIZ];
    229	int err;
    230
    231	argc = parse_options(argc, argv, options, bench_usage, 0);
    232	if (argc) {
    233		usage_with_options(bench_usage, options);
    234		exit(EXIT_FAILURE);
    235	}
    236
    237	err = target__validate(&opts.target);
    238	if (err) {
    239		target__strerror(&opts.target, err, errbuf, sizeof(errbuf));
    240		pr_err("%s\n", errbuf);
    241		goto out;
    242	}
    243
    244	err = target__parse_uid(&opts.target);
    245	if (err) {
    246		target__strerror(&opts.target, err, errbuf, sizeof(errbuf));
    247		pr_err("%s", errbuf);
    248		goto out;
    249	}
    250
    251	/* Enable ignoring missing threads when -u/-p option is defined. */
    252	opts.ignore_missing_thread = opts.target.uid != UINT_MAX || opts.target.pid;
    253
    254	evstr = bench__repeat_event_string(event_string, nr_events);
    255	if (!evstr) {
    256		err = -ENOMEM;
    257		goto out;
    258	}
    259
    260	err = bench_evlist_open_close__run(evstr);
    261
    262	free(evstr);
    263out:
    264	return err;
    265}