Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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	ASSIGN(machine_pid);
499	ASSIGN(vcpu);
500
501	if (sample->branch_stack) {
502		d_sample.brstack_nr = sample->branch_stack->nr;
503		d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
504	} else {
505		d_sample.brstack_nr = 0;
506		d_sample.brstack = NULL;
507	}
508
509	if (sample->callchain) {
510		d_sample.raw_callchain_nr = sample->callchain->nr;
511		d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
512	} else {
513		d_sample.raw_callchain_nr = 0;
514		d_sample.raw_callchain = NULL;
515	}
516
517	d_sample.addr_correlates_sym =
518		(evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
519		sample_addr_correlates_sym(&evsel->core.attr);
520
521	d_sample.event = evsel__name(evsel);
522
523	d->ctx_valid = true;
524
525	if (early)
526		ret = d->filter_event_early(d->data, &d_sample, d);
527	else
528		ret = d->filter_event(d->data, &d_sample, d);
529
530	d->ctx_valid = false;
531
532	return ret;
533}
534
535bool get_filter_desc(const char *dirname, const char *name, char **desc,
536		     char **long_desc)
537{
538	char path[PATH_MAX];
539	void *handle;
540	const char *(*desc_fn)(const char **long_description);
541
542	snprintf(path, sizeof(path), "%s/%s", dirname, name);
543	handle = dlopen(path, RTLD_NOW);
544	if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
545		return false;
546	desc_fn = dlsym(handle, "filter_description");
547	if (desc_fn) {
548		const char *dsc;
549		const char *long_dsc;
550
551		dsc = desc_fn(&long_dsc);
552		if (dsc)
553			*desc = strdup(dsc);
554		if (long_dsc)
555			*long_desc = strdup(long_dsc);
556	}
557	dlclose(handle);
558	return true;
559}
560
561static void list_filters(const char *dirname)
562{
563	struct dirent *entry;
564	DIR *dir;
565
566	dir = opendir(dirname);
567	if (!dir)
568		return;
569
570	while ((entry = readdir(dir)) != NULL)
571	{
572		size_t n = strlen(entry->d_name);
573		char *long_desc = NULL;
574		char *desc = NULL;
575
576		if (entry->d_type == DT_DIR || n < 4 ||
577		    strcmp(".so", entry->d_name + n - 3))
578			continue;
579		if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
580			continue;
581		printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
582		if (verbose > 0) {
583			char *p = long_desc;
584			char *line;
585
586			while ((line = strsep(&p, "\n")) != NULL)
587				printf("%39s%s\n", "", line);
588		}
589		free(long_desc);
590		free(desc);
591	}
592
593	closedir(dir);
594}
595
596int list_available_dlfilters(const struct option *opt __maybe_unused,
597			     const char *s __maybe_unused,
598			     int unset __maybe_unused)
599{
600	char path[PATH_MAX];
601	char *exec_path;
602
603	printf("List of available dlfilters:\n");
604
605	list_filters(".");
606
607	exec_path = get_argv_exec_path();
608	if (!exec_path)
609		goto out;
610	snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
611
612	list_filters(path);
613
614	free(exec_path);
615out:
616	exit(0);
617}