File Index
Files and directories
Sign in
FILE

dlfilter.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/util/dlfilter.c C
  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#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		else
 39			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		else
 58			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	else
150		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						  /*native_arch=*/true);
239		}
240	}
241
242	if (!d->sample->insn_len)
243		return NULL;
244
245	*len = d->sample->insn_len;
246
247	return (__u8 *)d->sample->insn;
248}
249
250static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
251{
252	struct dlfilter *d = (struct dlfilter *)ctx;
253	struct addr_location *al;
254	unsigned int line = 0;
255	char *srcfile = NULL;
256	struct map *map;
257	struct dso *dso;
258	u64 addr;
259
260	if (!d->ctx_valid || !line_no)
261		return NULL;
262
263	al = get_al(d);
264	if (!al)
265		return NULL;
266
267	map = al->map;
268	addr = al->addr;
269	dso = map ? map__dso(map) : NULL;
270
271	if (dso)
272		srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line);
273
274	*line_no = line;
275	return srcfile;
276}
277
278static struct perf_event_attr *dlfilter__attr(void *ctx)
279{
280	struct dlfilter *d = (struct dlfilter *)ctx;
281
282	if (!d->ctx_valid)
283		return NULL;
284
285	return &d->evsel->core.attr;
286}
287
288static __s32 code_read(__u64 ip, struct map *map, struct machine *machine, void *buf, __u32 len)
289{
290	u64 offset = map__map_ip(map, ip);
291
292	if (ip + len >= map__end(map))
293		len = map__end(map) - ip;
294
295	return dso__data_read_offset(map__dso(map), machine, offset, buf, len);
296}
297
298static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
299{
300	struct dlfilter *d = (struct dlfilter *)ctx;
301	struct addr_location *al;
302	struct addr_location a;
303	__s32 ret;
304
305	if (!d->ctx_valid)
306		return -1;
307
308	al = get_al(d);
309	if (!al)
310		return -1;
311
312	if (al->map && ip >= map__start(al->map) && ip < map__end(al->map) &&
313	    machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
314		return code_read(ip, al->map, d->machine, buf, len);
315
316	addr_location__init(&a);
317
318	thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a);
319	ret = a.map ? code_read(ip, a.map, d->machine, buf, len) : -1;
320
321	addr_location__exit(&a);
322
323	return ret;
324}
325
326static const struct perf_dlfilter_fns perf_dlfilter_fns = {
327	.resolve_ip      = dlfilter__resolve_ip,
328	.resolve_addr    = dlfilter__resolve_addr,
329	.args            = dlfilter__args,
330	.resolve_address = dlfilter__resolve_address,
331	.al_cleanup      = dlfilter__al_cleanup,
332	.insn            = dlfilter__insn,
333	.srcline         = dlfilter__srcline,
334	.attr            = dlfilter__attr,
335	.object_code     = dlfilter__object_code,
336};
337
338static char *find_dlfilter(const char *file)
339{
340	char path[PATH_MAX];
341	char *exec_path;
342
343	if (strchr(file, '/'))
344		goto out;
345
346	if (!access(file, R_OK)) {
347		/*
348		 * Prepend "./" so that dlopen will find the file in the
349		 * current directory.
350		 */
351		snprintf(path, sizeof(path), "./%s", file);
352		file = path;
353		goto out;
354	}
355
356	exec_path = get_argv_exec_path();
357	if (!exec_path)
358		goto out;
359	snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
360	free(exec_path);
361	if (!access(path, R_OK))
362		file = path;
363out:
364	return strdup(file);
365}
366
367#define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
368
369static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
370{
371	CHECK_FLAG(BRANCH);
372	CHECK_FLAG(CALL);
373	CHECK_FLAG(RETURN);
374	CHECK_FLAG(CONDITIONAL);
375	CHECK_FLAG(SYSCALLRET);
376	CHECK_FLAG(ASYNC);
377	CHECK_FLAG(INTERRUPT);
378	CHECK_FLAG(TX_ABORT);
379	CHECK_FLAG(TRACE_BEGIN);
380	CHECK_FLAG(TRACE_END);
381	CHECK_FLAG(IN_TX);
382	CHECK_FLAG(VMENTRY);
383	CHECK_FLAG(VMEXIT);
384
385	memset(d, 0, sizeof(*d));
386	d->file = find_dlfilter(file);
387	if (!d->file)
388		return -1;
389	d->dlargc = dlargc;
390	d->dlargv = dlargv;
391	return 0;
392}
393
394static void dlfilter__exit(struct dlfilter *d)
395{
396	zfree(&d->file);
397}
398
399static int dlfilter__open(struct dlfilter *d)
400{
401	d->handle = dlopen(d->file, RTLD_NOW);
402	if (!d->handle) {
403		pr_err("dlopen failed for: '%s'\n", d->file);
404		return -1;
405	}
406	d->start = dlsym(d->handle, "start");
407	d->filter_event = dlsym(d->handle, "filter_event");
408	d->filter_event_early = dlsym(d->handle, "filter_event_early");
409	d->stop = dlsym(d->handle, "stop");
410	d->fns = dlsym(d->handle, "perf_dlfilter_fns");
411	if (d->fns)
412		memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
413	return 0;
414}
415
416static int dlfilter__close(struct dlfilter *d)
417{
418	return dlclose(d->handle);
419}
420
421struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
422{
423	struct dlfilter *d = malloc(sizeof(*d));
424
425	if (!d)
426		return NULL;
427
428	if (dlfilter__init(d, file, dlargc, dlargv))
429		goto err_free;
430
431	if (dlfilter__open(d))
432		goto err_exit;
433
434	return d;
435
436err_exit:
437	dlfilter__exit(d);
438err_free:
439	free(d);
440	return NULL;
441}
442
443static void dlfilter__free(struct dlfilter *d)
444{
445	if (d) {
446		dlfilter__exit(d);
447		free(d);
448	}
449}
450
451int dlfilter__start(struct dlfilter *d, struct perf_session *session)
452{
453	if (d) {
454		d->session = session;
455		if (d->start) {
456			int ret;
457
458			d->in_start = true;
459			ret = d->start(&d->data, d);
460			d->in_start = false;
461			return ret;
462		}
463	}
464	return 0;
465}
466
467static int dlfilter__stop(struct dlfilter *d)
468{
469	if (d && d->stop) {
470		int ret;
471
472		d->in_stop = true;
473		ret = d->stop(d->data, d);
474		d->in_stop = false;
475		return ret;
476	}
477	return 0;
478}
479
480void dlfilter__cleanup(struct dlfilter *d)
481{
482	if (d) {
483		dlfilter__stop(d);
484		dlfilter__close(d);
485		dlfilter__free(d);
486	}
487}
488
489#define ASSIGN(x) d_sample.x = sample->x
490
491int dlfilter__do_filter_event(struct dlfilter *d,
492			      union perf_event *event,
493			      struct perf_sample *sample,
494			      struct evsel *evsel,
495			      struct machine *machine,
496			      struct addr_location *al,
497			      struct addr_location *addr_al,
498			      bool early)
499{
500	struct perf_dlfilter_sample d_sample;
501	struct perf_dlfilter_al d_ip_al;
502	struct perf_dlfilter_al d_addr_al;
503	int ret;
504
505	d->event       = event;
506	d->sample      = sample;
507	d->evsel       = evsel;
508	d->machine     = machine;
509	d->al          = al;
510	d->addr_al     = addr_al;
511	d->d_sample    = &d_sample;
512	d->d_ip_al     = &d_ip_al;
513	d->d_addr_al   = &d_addr_al;
514
515	d_sample.size  = sizeof(d_sample);
516	d_sample.p_stage_cyc = sample->weight3;
517	d_ip_al.size   = 0; /* To indicate d_ip_al is not initialized */
518	d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
519
520	ASSIGN(ip);
521	ASSIGN(pid);
522	ASSIGN(tid);
523	ASSIGN(time);
524	ASSIGN(addr);
525	ASSIGN(id);
526	ASSIGN(stream_id);
527	ASSIGN(period);
528	ASSIGN(weight);
529	ASSIGN(ins_lat);
530	ASSIGN(transaction);
531	ASSIGN(insn_cnt);
532	ASSIGN(cyc_cnt);
533	ASSIGN(cpu);
534	ASSIGN(flags);
535	ASSIGN(data_src);
536	ASSIGN(phys_addr);
537	ASSIGN(data_page_size);
538	ASSIGN(code_page_size);
539	ASSIGN(cgroup);
540	ASSIGN(cpumode);
541	ASSIGN(misc);
542	ASSIGN(raw_size);
543	ASSIGN(raw_data);
544	ASSIGN(machine_pid);
545	ASSIGN(vcpu);
546
547	if (sample->branch_stack) {
548		d_sample.brstack_nr = sample->branch_stack->nr;
549		d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
550	} else {
551		d_sample.brstack_nr = 0;
552		d_sample.brstack = NULL;
553	}
554
555	if (sample->callchain) {
556		d_sample.raw_callchain_nr = sample->callchain->nr;
557		d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
558	} else {
559		d_sample.raw_callchain_nr = 0;
560		d_sample.raw_callchain = NULL;
561	}
562
563	d_sample.addr_correlates_sym =
564		(evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
565		sample_addr_correlates_sym(&evsel->core.attr);
566
567	d_sample.event = evsel__name(evsel);
568
569	d->ctx_valid = true;
570
571	if (early)
572		ret = d->filter_event_early(d->data, &d_sample, d);
573	else
574		ret = d->filter_event(d->data, &d_sample, d);
575
576	d->ctx_valid = false;
577
578	return ret;
579}
580
581bool get_filter_desc(const char *dirname, const char *name, char **desc,
582		     char **long_desc)
583{
584	char path[PATH_MAX];
585	void *handle;
586	const char *(*desc_fn)(const char **long_description);
587
588	snprintf(path, sizeof(path), "%s/%s", dirname, name);
589	handle = dlopen(path, RTLD_NOW);
590	if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
591		return false;
592	desc_fn = dlsym(handle, "filter_description");
593	if (desc_fn) {
594		const char *dsc;
595		const char *long_dsc;
596
597		dsc = desc_fn(&long_dsc);
598		if (dsc)
599			*desc = strdup(dsc);
600		if (long_dsc)
601			*long_desc = strdup(long_dsc);
602	}
603	dlclose(handle);
604	return true;
605}
606
607static void list_filters(const char *dirname)
608{
609	struct dirent *entry;
610	DIR *dir;
611
612	dir = opendir(dirname);
613	if (!dir)
614		return;
615
616	while ((entry = readdir(dir)) != NULL)
617	{
618		size_t n = strlen(entry->d_name);
619		char *long_desc = NULL;
620		char *desc = NULL;
621
622		if (entry->d_type == DT_DIR || n < 4 ||
623		    strcmp(".so", entry->d_name + n - 3))
624			continue;
625		if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
626			continue;
627		printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
628		if (verbose > 0) {
629			char *p = long_desc;
630			char *line;
631
632			while ((line = strsep(&p, "\n")) != NULL)
633				printf("%39s%s\n", "", line);
634		}
635		free(long_desc);
636		free(desc);
637	}
638
639	closedir(dir);
640}
641
642int list_available_dlfilters(const struct option *opt __maybe_unused,
643			     const char *s __maybe_unused,
644			     int unset __maybe_unused)
645{
646	char path[PATH_MAX];
647	char *exec_path;
648
649	printf("List of available dlfilters:\n");
650
651	list_filters(".");
652
653	exec_path = get_argv_exec_path();
654	if (!exec_path)
655		goto out;
656	snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
657
658	list_filters(path);
659
660	free(exec_path);
661out:
662	exit(0);
663}
~/ Read-only file preview.