brintos

brintos / linux-shallow public Read only

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