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

dlfilter.c (13033B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * dlfilter.c: Interface to perf script --dlfilter shared object
      4 * Copyright (c) 2021, Intel Corporation.
      5 */
      6#include <dlfcn.h>
      7#include <stdlib.h>
      8#include <string.h>
      9#include <dirent.h>
     10#include <subcmd/exec-cmd.h>
     11#include <linux/zalloc.h>
     12#include <linux/build_bug.h>
     13
     14#include "debug.h"
     15#include "event.h"
     16#include "evsel.h"
     17#include "dso.h"
     18#include "map.h"
     19#include "thread.h"
     20#include "trace-event.h"
     21#include "symbol.h"
     22#include "srcline.h"
     23#include "dlfilter.h"
     24#include "../include/perf/perf_dlfilter.h"
     25
     26static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
     27{
     28	struct symbol *sym = al->sym;
     29
     30	d_al->size = sizeof(*d_al);
     31	if (al->map) {
     32		struct dso *dso = al->map->dso;
     33
     34		if (symbol_conf.show_kernel_path && dso->long_name)
     35			d_al->dso = dso->long_name;
     36		else
     37			d_al->dso = dso->name;
     38		d_al->is_64_bit = dso->is_64_bit;
     39		d_al->buildid_size = dso->bid.size;
     40		d_al->buildid = dso->bid.data;
     41	} else {
     42		d_al->dso = NULL;
     43		d_al->is_64_bit = 0;
     44		d_al->buildid_size = 0;
     45		d_al->buildid = NULL;
     46	}
     47	if (sym) {
     48		d_al->sym = sym->name;
     49		d_al->sym_start = sym->start;
     50		d_al->sym_end = sym->end;
     51		if (al->addr < sym->end)
     52			d_al->symoff = al->addr - sym->start;
     53		else
     54			d_al->symoff = al->addr - al->map->start - sym->start;
     55		d_al->sym_binding = sym->binding;
     56	} else {
     57		d_al->sym = NULL;
     58		d_al->sym_start = 0;
     59		d_al->sym_end = 0;
     60		d_al->symoff = 0;
     61		d_al->sym_binding = 0;
     62	}
     63	d_al->addr = al->addr;
     64	d_al->comm = NULL;
     65	d_al->filtered = 0;
     66}
     67
     68static struct addr_location *get_al(struct dlfilter *d)
     69{
     70	struct addr_location *al = d->al;
     71
     72	if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
     73		return NULL;
     74	return al;
     75}
     76
     77static struct thread *get_thread(struct dlfilter *d)
     78{
     79	struct addr_location *al = get_al(d);
     80
     81	return al ? al->thread : NULL;
     82}
     83
     84static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
     85{
     86	struct dlfilter *d = (struct dlfilter *)ctx;
     87	struct perf_dlfilter_al *d_al = d->d_ip_al;
     88	struct addr_location *al;
     89
     90	if (!d->ctx_valid)
     91		return NULL;
     92
     93	/* 'size' is also used to indicate already initialized */
     94	if (d_al->size)
     95		return d_al;
     96
     97	al = get_al(d);
     98	if (!al)
     99		return NULL;
    100
    101	al_to_d_al(al, d_al);
    102
    103	d_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->ip);
    104	d_al->comm = al->thread ? thread__comm_str(al->thread) : ":-1";
    105	d_al->filtered = al->filtered;
    106
    107	return d_al;
    108}
    109
    110static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
    111{
    112	struct dlfilter *d = (struct dlfilter *)ctx;
    113	struct perf_dlfilter_al *d_addr_al = d->d_addr_al;
    114	struct addr_location *addr_al = d->addr_al;
    115
    116	if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
    117		return NULL;
    118
    119	/* 'size' is also used to indicate already initialized */
    120	if (d_addr_al->size)
    121		return d_addr_al;
    122
    123	if (!addr_al->thread) {
    124		struct thread *thread = get_thread(d);
    125
    126		if (!thread)
    127			return NULL;
    128		thread__resolve(thread, addr_al, d->sample);
    129	}
    130
    131	al_to_d_al(addr_al, d_addr_al);
    132
    133	d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
    134
    135	return d_addr_al;
    136}
    137
    138static char **dlfilter__args(void *ctx, int *dlargc)
    139{
    140	struct dlfilter *d = (struct dlfilter *)ctx;
    141
    142	if (dlargc)
    143		*dlargc = 0;
    144	else
    145		return NULL;
    146
    147	if (!d->ctx_valid && !d->in_start && !d->in_stop)
    148		return NULL;
    149
    150	*dlargc = d->dlargc;
    151	return d->dlargv;
    152}
    153
    154static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
    155{
    156	struct dlfilter *d = (struct dlfilter *)ctx;
    157	struct perf_dlfilter_al d_al;
    158	struct addr_location al;
    159	struct thread *thread;
    160	__u32 sz;
    161
    162	if (!d->ctx_valid || !d_al_p)
    163		return -1;
    164
    165	thread = get_thread(d);
    166	if (!thread)
    167		return -1;
    168
    169	thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
    170
    171	al_to_d_al(&al, &d_al);
    172
    173	d_al.is_kernel_ip = machine__kernel_ip(d->machine, address);
    174
    175	sz = d_al_p->size;
    176	memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
    177	d_al_p->size = sz;
    178
    179	return 0;
    180}
    181
    182static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
    183{
    184	struct dlfilter *d = (struct dlfilter *)ctx;
    185
    186	if (!len)
    187		return NULL;
    188
    189	*len = 0;
    190
    191	if (!d->ctx_valid)
    192		return NULL;
    193
    194	if (d->sample->ip && !d->sample->insn_len) {
    195		struct addr_location *al = d->al;
    196
    197		if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
    198			return NULL;
    199
    200		if (al->thread->maps && al->thread->maps->machine)
    201			script_fetch_insn(d->sample, al->thread, al->thread->maps->machine);
    202	}
    203
    204	if (!d->sample->insn_len)
    205		return NULL;
    206
    207	*len = d->sample->insn_len;
    208
    209	return (__u8 *)d->sample->insn;
    210}
    211
    212static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
    213{
    214	struct dlfilter *d = (struct dlfilter *)ctx;
    215	struct addr_location *al;
    216	unsigned int line = 0;
    217	char *srcfile = NULL;
    218	struct map *map;
    219	u64 addr;
    220
    221	if (!d->ctx_valid || !line_no)
    222		return NULL;
    223
    224	al = get_al(d);
    225	if (!al)
    226		return NULL;
    227
    228	map = al->map;
    229	addr = al->addr;
    230
    231	if (map && map->dso)
    232		srcfile = get_srcline_split(map->dso, map__rip_2objdump(map, addr), &line);
    233
    234	*line_no = line;
    235	return srcfile;
    236}
    237
    238static struct perf_event_attr *dlfilter__attr(void *ctx)
    239{
    240	struct dlfilter *d = (struct dlfilter *)ctx;
    241
    242	if (!d->ctx_valid)
    243		return NULL;
    244
    245	return &d->evsel->core.attr;
    246}
    247
    248static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
    249{
    250	struct dlfilter *d = (struct dlfilter *)ctx;
    251	struct addr_location *al;
    252	struct addr_location a;
    253	struct map *map;
    254	u64 offset;
    255
    256	if (!d->ctx_valid)
    257		return -1;
    258
    259	al = get_al(d);
    260	if (!al)
    261		return -1;
    262
    263	map = al->map;
    264
    265	if (map && ip >= map->start && ip < map->end &&
    266	    machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
    267		goto have_map;
    268
    269	thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a);
    270	if (!a.map)
    271		return -1;
    272
    273	map = a.map;
    274have_map:
    275	offset = map->map_ip(map, ip);
    276	if (ip + len >= map->end)
    277		len = map->end - ip;
    278	return dso__data_read_offset(map->dso, d->machine, offset, buf, len);
    279}
    280
    281static const struct perf_dlfilter_fns perf_dlfilter_fns = {
    282	.resolve_ip      = dlfilter__resolve_ip,
    283	.resolve_addr    = dlfilter__resolve_addr,
    284	.args            = dlfilter__args,
    285	.resolve_address = dlfilter__resolve_address,
    286	.insn            = dlfilter__insn,
    287	.srcline         = dlfilter__srcline,
    288	.attr            = dlfilter__attr,
    289	.object_code     = dlfilter__object_code,
    290};
    291
    292static char *find_dlfilter(const char *file)
    293{
    294	char path[PATH_MAX];
    295	char *exec_path;
    296
    297	if (strchr(file, '/'))
    298		goto out;
    299
    300	if (!access(file, R_OK)) {
    301		/*
    302		 * Prepend "./" so that dlopen will find the file in the
    303		 * current directory.
    304		 */
    305		snprintf(path, sizeof(path), "./%s", file);
    306		file = path;
    307		goto out;
    308	}
    309
    310	exec_path = get_argv_exec_path();
    311	if (!exec_path)
    312		goto out;
    313	snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
    314	free(exec_path);
    315	if (!access(path, R_OK))
    316		file = path;
    317out:
    318	return strdup(file);
    319}
    320
    321#define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
    322
    323static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
    324{
    325	CHECK_FLAG(BRANCH);
    326	CHECK_FLAG(CALL);
    327	CHECK_FLAG(RETURN);
    328	CHECK_FLAG(CONDITIONAL);
    329	CHECK_FLAG(SYSCALLRET);
    330	CHECK_FLAG(ASYNC);
    331	CHECK_FLAG(INTERRUPT);
    332	CHECK_FLAG(TX_ABORT);
    333	CHECK_FLAG(TRACE_BEGIN);
    334	CHECK_FLAG(TRACE_END);
    335	CHECK_FLAG(IN_TX);
    336	CHECK_FLAG(VMENTRY);
    337	CHECK_FLAG(VMEXIT);
    338
    339	memset(d, 0, sizeof(*d));
    340	d->file = find_dlfilter(file);
    341	if (!d->file)
    342		return -1;
    343	d->dlargc = dlargc;
    344	d->dlargv = dlargv;
    345	return 0;
    346}
    347
    348static void dlfilter__exit(struct dlfilter *d)
    349{
    350	zfree(&d->file);
    351}
    352
    353static int dlfilter__open(struct dlfilter *d)
    354{
    355	d->handle = dlopen(d->file, RTLD_NOW);
    356	if (!d->handle) {
    357		pr_err("dlopen failed for: '%s'\n", d->file);
    358		return -1;
    359	}
    360	d->start = dlsym(d->handle, "start");
    361	d->filter_event = dlsym(d->handle, "filter_event");
    362	d->filter_event_early = dlsym(d->handle, "filter_event_early");
    363	d->stop = dlsym(d->handle, "stop");
    364	d->fns = dlsym(d->handle, "perf_dlfilter_fns");
    365	if (d->fns)
    366		memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
    367	return 0;
    368}
    369
    370static int dlfilter__close(struct dlfilter *d)
    371{
    372	return dlclose(d->handle);
    373}
    374
    375struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
    376{
    377	struct dlfilter *d = malloc(sizeof(*d));
    378
    379	if (!d)
    380		return NULL;
    381
    382	if (dlfilter__init(d, file, dlargc, dlargv))
    383		goto err_free;
    384
    385	if (dlfilter__open(d))
    386		goto err_exit;
    387
    388	return d;
    389
    390err_exit:
    391	dlfilter__exit(d);
    392err_free:
    393	free(d);
    394	return NULL;
    395}
    396
    397static void dlfilter__free(struct dlfilter *d)
    398{
    399	if (d) {
    400		dlfilter__exit(d);
    401		free(d);
    402	}
    403}
    404
    405int dlfilter__start(struct dlfilter *d, struct perf_session *session)
    406{
    407	if (d) {
    408		d->session = session;
    409		if (d->start) {
    410			int ret;
    411
    412			d->in_start = true;
    413			ret = d->start(&d->data, d);
    414			d->in_start = false;
    415			return ret;
    416		}
    417	}
    418	return 0;
    419}
    420
    421static int dlfilter__stop(struct dlfilter *d)
    422{
    423	if (d && d->stop) {
    424		int ret;
    425
    426		d->in_stop = true;
    427		ret = d->stop(d->data, d);
    428		d->in_stop = false;
    429		return ret;
    430	}
    431	return 0;
    432}
    433
    434void dlfilter__cleanup(struct dlfilter *d)
    435{
    436	if (d) {
    437		dlfilter__stop(d);
    438		dlfilter__close(d);
    439		dlfilter__free(d);
    440	}
    441}
    442
    443#define ASSIGN(x) d_sample.x = sample->x
    444
    445int dlfilter__do_filter_event(struct dlfilter *d,
    446			      union perf_event *event,
    447			      struct perf_sample *sample,
    448			      struct evsel *evsel,
    449			      struct machine *machine,
    450			      struct addr_location *al,
    451			      struct addr_location *addr_al,
    452			      bool early)
    453{
    454	struct perf_dlfilter_sample d_sample;
    455	struct perf_dlfilter_al d_ip_al;
    456	struct perf_dlfilter_al d_addr_al;
    457	int ret;
    458
    459	d->event       = event;
    460	d->sample      = sample;
    461	d->evsel       = evsel;
    462	d->machine     = machine;
    463	d->al          = al;
    464	d->addr_al     = addr_al;
    465	d->d_sample    = &d_sample;
    466	d->d_ip_al     = &d_ip_al;
    467	d->d_addr_al   = &d_addr_al;
    468
    469	d_sample.size  = sizeof(d_sample);
    470	d_ip_al.size   = 0; /* To indicate d_ip_al is not initialized */
    471	d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
    472
    473	ASSIGN(ip);
    474	ASSIGN(pid);
    475	ASSIGN(tid);
    476	ASSIGN(time);
    477	ASSIGN(addr);
    478	ASSIGN(id);
    479	ASSIGN(stream_id);
    480	ASSIGN(period);
    481	ASSIGN(weight);
    482	ASSIGN(ins_lat);
    483	ASSIGN(p_stage_cyc);
    484	ASSIGN(transaction);
    485	ASSIGN(insn_cnt);
    486	ASSIGN(cyc_cnt);
    487	ASSIGN(cpu);
    488	ASSIGN(flags);
    489	ASSIGN(data_src);
    490	ASSIGN(phys_addr);
    491	ASSIGN(data_page_size);
    492	ASSIGN(code_page_size);
    493	ASSIGN(cgroup);
    494	ASSIGN(cpumode);
    495	ASSIGN(misc);
    496	ASSIGN(raw_size);
    497	ASSIGN(raw_data);
    498
    499	if (sample->branch_stack) {
    500		d_sample.brstack_nr = sample->branch_stack->nr;
    501		d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
    502	} else {
    503		d_sample.brstack_nr = 0;
    504		d_sample.brstack = NULL;
    505	}
    506
    507	if (sample->callchain) {
    508		d_sample.raw_callchain_nr = sample->callchain->nr;
    509		d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
    510	} else {
    511		d_sample.raw_callchain_nr = 0;
    512		d_sample.raw_callchain = NULL;
    513	}
    514
    515	d_sample.addr_correlates_sym =
    516		(evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
    517		sample_addr_correlates_sym(&evsel->core.attr);
    518
    519	d_sample.event = evsel__name(evsel);
    520
    521	d->ctx_valid = true;
    522
    523	if (early)
    524		ret = d->filter_event_early(d->data, &d_sample, d);
    525	else
    526		ret = d->filter_event(d->data, &d_sample, d);
    527
    528	d->ctx_valid = false;
    529
    530	return ret;
    531}
    532
    533bool get_filter_desc(const char *dirname, const char *name, char **desc,
    534		     char **long_desc)
    535{
    536	char path[PATH_MAX];
    537	void *handle;
    538	const char *(*desc_fn)(const char **long_description);
    539
    540	snprintf(path, sizeof(path), "%s/%s", dirname, name);
    541	handle = dlopen(path, RTLD_NOW);
    542	if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
    543		return false;
    544	desc_fn = dlsym(handle, "filter_description");
    545	if (desc_fn) {
    546		const char *dsc;
    547		const char *long_dsc;
    548
    549		dsc = desc_fn(&long_dsc);
    550		if (dsc)
    551			*desc = strdup(dsc);
    552		if (long_dsc)
    553			*long_desc = strdup(long_dsc);
    554	}
    555	dlclose(handle);
    556	return true;
    557}
    558
    559static void list_filters(const char *dirname)
    560{
    561	struct dirent *entry;
    562	DIR *dir;
    563
    564	dir = opendir(dirname);
    565	if (!dir)
    566		return;
    567
    568	while ((entry = readdir(dir)) != NULL)
    569	{
    570		size_t n = strlen(entry->d_name);
    571		char *long_desc = NULL;
    572		char *desc = NULL;
    573
    574		if (entry->d_type == DT_DIR || n < 4 ||
    575		    strcmp(".so", entry->d_name + n - 3))
    576			continue;
    577		if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
    578			continue;
    579		printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
    580		if (verbose) {
    581			char *p = long_desc;
    582			char *line;
    583
    584			while ((line = strsep(&p, "\n")) != NULL)
    585				printf("%39s%s\n", "", line);
    586		}
    587		free(long_desc);
    588		free(desc);
    589	}
    590
    591	closedir(dir);
    592}
    593
    594int list_available_dlfilters(const struct option *opt __maybe_unused,
    595			     const char *s __maybe_unused,
    596			     int unset __maybe_unused)
    597{
    598	char path[PATH_MAX];
    599	char *exec_path;
    600
    601	printf("List of available dlfilters:\n");
    602
    603	list_filters(".");
    604
    605	exec_path = get_argv_exec_path();
    606	if (!exec_path)
    607		goto out;
    608	snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
    609
    610	list_filters(path);
    611
    612	free(exec_path);
    613out:
    614	exit(0);
    615}