lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <YNIsr412oJu2qGpA@kernel.org>
Date:   Tue, 22 Jun 2021 15:32:15 -0300
From:   Arnaldo Carvalho de Melo <acme@...nel.org>
To:     Adrian Hunter <adrian.hunter@...el.com>
Cc:     Jiri Olsa <jolsa@...hat.com>, Andi Kleen <ak@...ux.intel.com>,
        Peter Zijlstra <peterz@...radead.org>,
        Ingo Molnar <mingo@...hat.com>,
        Mark Rutland <mark.rutland@....com>,
        Namhyung Kim <namhyung@...nel.org>,
        Leo Yan <leo.yan@...aro.org>,
        Kan Liang <kan.liang@...ux.intel.com>,
        linux-perf-users@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH RFC 04/11] perf script: Add API for filtering via
 dynamically loaded shared object

Em Mon, Jun 21, 2021 at 06:05:07PM +0300, Adrian Hunter escreveu:
> In some cases, users want to filter very large amounts of data (e.g. from
> AUX area tracing like Intel PT) looking for something specific. While
> scripting such as Python can be used, Python is 10 to 20 times slower than
> C. So define a C API so that custom filters can be written and loaded.

Statically linking with perf would be even faster 8-) But yeah, I think
its something useful, some notes below

A first question, can this be combined with pre-existing filters? I.e.

  perf script -C 0 --dlfilter file.so

?

Also in the docs we can state that after these custom filters get
polished and mature, it would be interesting to send them to be merged
upstream, where they could even be statically linked with perf, if a
profile session with it linked statically proves that.

Having a collection of such filters upstream we could then have
a similar interface as for the script collection to describe filters,
i.e. the .so file would have to have a description and perhaps a man
page:

⬢[acme@...lbox perf]$ perf script -l
List of available trace scripts:
  compaction-times [-h] [-u] [-p|-pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-regex] display time taken by mm compaction
  event_analyzing_sample               analyze all perf samples
  export-to-postgresql [database name] [columns] [calls] export perf data to a postgresql database
  export-to-sqlite [database name] [columns] [calls] export perf data to a sqlite3 database
  failed-syscalls-by-pid [comm]        system-wide failed syscalls, by pid
  flamegraph                           create flame graphs
  futex-contention                     futext contention measurement
  intel-pt-events                      print Intel PT Events including Power Events and PTWRITE
  mem-phys-addr                        resolve physical address samples
  net_dropmonitor                      display a table of dropped frames
  netdev-times [tx] [rx] [dev=] [debug] display a process of packet and processing time
  powerpc-hcalls
  sched-migration                      sched migration overview
  sctop [comm] [interval]              syscall top
  stackcollapse                        produce callgraphs in short form for scripting use
  syscall-counts-by-pid [comm]         system-wide syscall counts, by pid
  syscall-counts [comm]                system-wide syscall counts
  failed-syscalls [comm]               system-wide failed syscalls
  rw-by-file <comm>                    r/w activity for a program, by file
  rw-by-pid                            system-wide r/w activity
  rwtop [interval]                     system-wide r/w top
  wakeup-latency                       system-wide min/max/avg wakeup latency
⬢[acme@...lbox perf]$

I.e. some 'perf script --list-dlfilters'
 
> Signed-off-by: Adrian Hunter <adrian.hunter@...el.com>
> ---
>  tools/perf/Documentation/perf-dlfilter.txt | 214 +++++++++++++
>  tools/perf/Documentation/perf-script.txt   |   7 +-
>  tools/perf/builtin-script.c                |  25 +-
>  tools/perf/util/Build                      |   1 +
>  tools/perf/util/dlfilter.c                 | 330 +++++++++++++++++++++
>  tools/perf/util/dlfilter.h                 |  74 +++++
>  tools/perf/util/perf_dlfilter.h            | 120 ++++++++
>  7 files changed, 769 insertions(+), 2 deletions(-)
>  create mode 100644 tools/perf/Documentation/perf-dlfilter.txt
>  create mode 100644 tools/perf/util/dlfilter.c
>  create mode 100644 tools/perf/util/dlfilter.h
>  create mode 100644 tools/perf/util/perf_dlfilter.h
> 
> diff --git a/tools/perf/Documentation/perf-dlfilter.txt b/tools/perf/Documentation/perf-dlfilter.txt
> new file mode 100644
> index 000000000000..d8f80998790f
> --- /dev/null
> +++ b/tools/perf/Documentation/perf-dlfilter.txt
> @@ -0,0 +1,214 @@
> +perf-dlfilter(1)
> +================
> +
> +NAME
> +----
> +perf-dlfilter - Filter sample events using a dynamically loaded shared
> +object file
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'perf script' [--dlfilter file.so ]
> +
> +DESCRIPTION
> +-----------
> +
> +This option is used to process data through a custom filter provided by a
> +dynamically loaded shared object file.
> +
> +API
> +---
> +
> +The API for filtering consists of the following:
> +
> +[source,c]
> +----
> +#include <perf/perf_dlfilter.h>
> +
> +const struct perf_dlfilter_fns perf_dlfilter_fns;
> +
> +int start(void **data);
> +int stop(void *data);
> +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx);
> +----
> +
> +If implemented, 'start' will be called at the beginning, before any
> +calls to 'filter_event' . Return 0 to indicate success,
> +or return a negative error code. '*data' can be assigned for use by other
> +functions.
> +
> +If implemented, 'stop' will be called at the end, after any calls to
> +'filter_event'. Return 0 to indicate success, or
> +return a negative error code. 'data' is set by 'start'.
> +
> +If implemented, 'filter_event' will be called for each sample event.
> +Return 0 to keep the sample event, 1 to filter it out, or return a negative
> +error code. 'data' is set by 'start'. 'ctx' is needed for calls to
> +'perf_dlfilter_fns'.
> +
> +The perf_dlfilter_sample structure
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +'filter_event' is passed a perf_dlfilter_sample
> +structure, which contains the following fields:
> +[source,c]
> +----
> +/*
> + * perf sample event information (as per perf script and <linux/perf_event.h>)
> + */
> +struct perf_dlfilter_sample {
> +	__u32 size; /* Size of this structure (for compatibility checking) */

There is a 4-byte hole here, 

> +	__u64 ip;
> +	__s32 pid;
> +	__s32 tid;
> +	__u64 time;
> +	__u64 addr;
> +	__u64 id;
> +	__u64 stream_id;
> +	__u64 period;
> +	__u64 weight;		/* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
> +	__u16 ins_lat;		/* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
> +	__u16 p_stage_cyc;	/* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */

Another

Can you move these last two __u16 to right after the 'size' field?

> +	__u64 transaction;	/* Refer PERF_SAMPLE_TRANSACTION in <linux/perf_event.h> */
> +	__u64 insn_cnt;	/* For instructions-per-cycle (IPC) */
> +	__u64 cyc_cnt;		/* For instructions-per-cycle (IPC) */
> +	__s32 cpu;
> +	__u32 flags;		/* Refer PERF_DLFILTER_FLAG_* above */
> +	__u64 data_src;		/* Refer PERF_SAMPLE_DATA_SRC in <linux/perf_event.h> */
> +	__u64 phys_addr;	/* Refer PERF_SAMPLE_PHYS_ADDR in <linux/perf_event.h> */
> +	__u64 data_page_size;	/* Refer PERF_SAMPLE_DATA_PAGE_SIZE in <linux/perf_event.h> */
> +	__u64 code_page_size;	/* Refer PERF_SAMPLE_CODE_PAGE_SIZE in <linux/perf_event.h> */
> +	__u64 cgroup;		/* Refer PERF_SAMPLE_CGROUP in <linux/perf_event.h> */
> +	__u8  cpumode;		/* Refer CPUMODE_MASK etc in <linux/perf_event.h> */
> +	__u8  addr_correlates_sym; /* True => resolve_addr() can be called */
> +	__u16 misc;		/* Refer perf_event_header in <linux/perf_event.h> */
> +	__u32 raw_size;		/* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */
> +	const void *raw_data;	/* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */
> +	__u64 brstack_nr;	/* Number of brstack entries */
> +	const struct perf_branch_entry *brstack; /* Refer <linux/perf_event.h> */
> +	__u64 raw_callchain_nr;	/* Number of raw_callchain entries */
> +	const __u64 *raw_callchain; /* Refer <linux/perf_event.h> */
> +	const char *event;
> +};
> +----
> +
> +The perf_dlfilter_fns structure
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The 'perf_dlfilter_fns' structure is populated with function pointers when the
> +file is loaded. The functions can be called by 'filter_event'.
> +
> +[source,c]
> +----
> +struct perf_dlfilter_fns {
> +	const struct perf_dlfilter_al *(*resolve_ip)(void *ctx);
> +	const struct perf_dlfilter_al *(*resolve_addr)(void *ctx);
> +	void *(*reserved[126])(void *);
> +};
> +----
> +
> +'resolve_ip' returns information about ip.
> +
> +'resolve_addr' returns information about addr (if addr_correlates_sym).
> +
> +The perf_dlfilter_al structure
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The 'perf_dlfilter_al' structure contains information about an address.
> +
> +[source,c]
> +----
> +/*
> + * Address location (as per perf script)
> + */
> +struct perf_dlfilter_al {
> +	__u32 size; /* Size of this structure (for compatibility checking) */
> +	__u32 symoff;
> +	const char *sym;
> +	__u64 addr; /* Mapped address (from dso) */
> +	__u64 sym_start;
> +	__u64 sym_end;
> +	const char *dso;
> +	__u8  sym_binding; /* STB_LOCAL, STB_GLOBAL or STB_WEAK, refer <elf.h> */
> +	__u8  is_64_bit; /* Only valid if dso is not NULL */
> +	__u8  is_kernel_ip; /* True if in kernel space */
> +	__u32 buildid_size;
> +	__u8 *buildid;
> +	/* Below members are only populated by resolve_ip() */
> +	__u8 filtered; /* true if this sample event will be filtered out */
> +	const char *comm;
> +};
> +----
> +
> +perf_dlfilter_sample flags
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The 'flags' member of 'perf_dlfilter_sample' corresponds with the flags field
> +of perf script. The bits of the flags are as follows:
> +
> +[source,c]
> +----
> +/* Definitions for perf_dlfilter_sample flags */
> +enum {
> +	PERF_DLFILTER_FLAG_BRANCH	= 1ULL << 0,
> +	PERF_DLFILTER_FLAG_CALL		= 1ULL << 1,
> +	PERF_DLFILTER_FLAG_RETURN	= 1ULL << 2,
> +	PERF_DLFILTER_FLAG_CONDITIONAL	= 1ULL << 3,
> +	PERF_DLFILTER_FLAG_SYSCALLRET	= 1ULL << 4,
> +	PERF_DLFILTER_FLAG_ASYNC	= 1ULL << 5,
> +	PERF_DLFILTER_FLAG_INTERRUPT	= 1ULL << 6,
> +	PERF_DLFILTER_FLAG_TX_ABORT	= 1ULL << 7,
> +	PERF_DLFILTER_FLAG_TRACE_BEGIN	= 1ULL << 8,
> +	PERF_DLFILTER_FLAG_TRACE_END	= 1ULL << 9,
> +	PERF_DLFILTER_FLAG_IN_TX	= 1ULL << 10,
> +	PERF_DLFILTER_FLAG_VMENTRY	= 1ULL << 11,
> +	PERF_DLFILTER_FLAG_VMEXIT	= 1ULL << 12,
> +};
> +----
> +
> +EXAMPLE
> +-------
> +
> +Filter out everything except branches from "foo" to "bar":
> +
> +[source,c]
> +----
> +#include <perf/perf_dlfilter.h>
> +#include <string.h>
> +
> +const struct perf_dlfilter_fns perf_dlfilter_fns;
> +
> +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx)
> +{
> +	const struct perf_dlfilter_al *al;
> +	const struct perf_dlfilter_al *addr_al;
> +
> +	if (!sample->ip || !sample->addr_correlates_sym)
> +		return 1;
> +
> +	al = perf_dlfilter_fns.resolve_ip(ctx);
> +	if (!al || !al->sym || strcmp(al->sym, "foo"))
> +		return 1;
> +
> +	addr_al = perf_dlfilter_fns.resolve_addr(ctx);
> +	if (!addr_al || !addr_al->sym || strcmp(addr_al->sym, "bar"))
> +		return 1;
> +
> +	return 0;
> +}
> +----
> +
> +To build the shared object, assuming perf has been installed for the local user
> +i.e. perf_dlfilter.h is in ~/include/perf :
> +
> +	gcc -c -I ~/include -fpic dlfilter-example.c
> +	gcc -shared -o dlfilter-example.so dlfilter-example.o
> +
> +To use the filter with perf script:
> +
> +	perf script --dlfilter ./dlfilter-example.so
> +
> +SEE ALSO
> +--------
> +linkperf:perf-script[1]
> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
> index 48a5f5b26dd4..2306c81b606b 100644
> --- a/tools/perf/Documentation/perf-script.txt
> +++ b/tools/perf/Documentation/perf-script.txt
> @@ -98,6 +98,10 @@ OPTIONS
>          Generate perf-script.[ext] starter script for given language,
>          using current perf.data.
>  
> +--dlfilter=<file>::
> +	Filter sample events using the given shared object file.
> +	Refer linkperf:perf-dlfilter[1]
> +
>  -a::
>          Force system-wide collection.  Scripts run without a <command>
>          normally use -a by default, while scripts run with a <command>
> @@ -483,4 +487,5 @@ include::itrace.txt[]
>  SEE ALSO
>  --------
>  linkperf:perf-record[1], linkperf:perf-script-perl[1],
> -linkperf:perf-script-python[1], linkperf:perf-intel-pt[1]
> +linkperf:perf-script-python[1], linkperf:perf-intel-pt[1],
> +linkperf:perf-dlfilter[1]
> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> index d2771a997e26..aaf2922643a0 100644
> --- a/tools/perf/builtin-script.c
> +++ b/tools/perf/builtin-script.c
> @@ -55,6 +55,7 @@
>  #include <subcmd/pager.h>
>  #include <perf/evlist.h>
>  #include <linux/err.h>
> +#include "util/dlfilter.h"
>  #include "util/record.h"
>  #include "util/util.h"
>  #include "perf.h"
> @@ -79,6 +80,7 @@ static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
>  static struct perf_stat_config	stat_config;
>  static int			max_blocks;
>  static bool			native_arch;
> +static struct dlfilter		*dlfilter;
>  
>  unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
>  
> @@ -2175,6 +2177,7 @@ static int process_sample_event(struct perf_tool *tool,
>  	struct perf_script *scr = container_of(tool, struct perf_script, tool);
>  	struct addr_location al;
>  	struct addr_location addr_al;
> +	int ret = 0;
>  
>  	if (perf_time__ranges_skip_sample(scr->ptime_range, scr->range_num,
>  					  sample->time)) {
> @@ -2213,6 +2216,13 @@ static int process_sample_event(struct perf_tool *tool,
>  	if (evswitch__discard(&scr->evswitch, evsel))
>  		goto out_put;
>  
> +	ret = dlfilter__filter_event(dlfilter, event, sample, evsel, machine, &al, &addr_al);
> +	if (ret) {
> +		if (ret > 0)
> +			ret = 0;
> +		goto out_put;
> +	}
> +
>  	if (scripting_ops) {
>  		struct addr_location *addr_al_ptr = NULL;
>  
> @@ -2229,7 +2239,7 @@ static int process_sample_event(struct perf_tool *tool,
>  
>  out_put:
>  	addr_location__put(&al);
> -	return 0;
> +	return ret;
>  }
>  
>  static int process_attr(struct perf_tool *tool, union perf_event *event,
> @@ -3568,6 +3578,7 @@ int cmd_script(int argc, const char **argv)
>  	};
>  	struct utsname uts;
>  	char *script_path = NULL;
> +	const char *dlfilter_file = NULL;
>  	const char **__argv;
>  	int i, j, err = 0;
>  	struct perf_script script = {
> @@ -3615,6 +3626,7 @@ int cmd_script(int argc, const char **argv)
>  		     parse_scriptname),
>  	OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
>  		   "generate perf-script.xx script in specified language"),
> +	OPT_STRING(0, "dlfilter", &dlfilter_file, "file", "filter .so file name"),
>  	OPT_STRING('i', "input", &input_name, "file", "input file name"),
>  	OPT_BOOLEAN('d', "debug-mode", &debug_mode,
>  		   "do various checks like samples ordering and lost events"),
> @@ -3933,6 +3945,12 @@ int cmd_script(int argc, const char **argv)
>  		exit(-1);
>  	}
>  
> +	if (dlfilter_file) {
> +		dlfilter = dlfilter__new(dlfilter_file);
> +		if (!dlfilter)
> +			return -1;
> +	}
> +
>  	if (!script_name) {
>  		setup_pager();
>  		use_browser = 0;
> @@ -4032,6 +4050,10 @@ int cmd_script(int argc, const char **argv)
>  		goto out_delete;
>  	}
>  
> +	err = dlfilter__start(dlfilter, session);
> +	if (err)
> +		goto out_delete;
> +
>  	if (script_name) {
>  		err = scripting_ops->start_script(script_name, argc, argv, session);
>  		if (err)
> @@ -4081,6 +4103,7 @@ int cmd_script(int argc, const char **argv)
>  
>  	if (script_started)
>  		cleanup_scripting();
> +	dlfilter__cleanup(dlfilter);
>  out:
>  	return err;
>  }
> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index 95e15d1035ab..1a909b53dc15 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -126,6 +126,7 @@ perf-y += parse-regs-options.o
>  perf-y += parse-sublevel-options.o
>  perf-y += term.o
>  perf-y += help-unknown-cmd.o
> +perf-y += dlfilter.o
>  perf-y += mem-events.o
>  perf-y += vsprintf.o
>  perf-y += units.o
> diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c
> new file mode 100644
> index 000000000000..15cb9de13a4b
> --- /dev/null
> +++ b/tools/perf/util/dlfilter.c
> @@ -0,0 +1,330 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * dlfilter.c: Interface to perf script --dlfilter shared object
> + * Copyright (c) 2021, Intel Corporation.
> + */
> +#include <dlfcn.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <linux/zalloc.h>
> +#include <linux/build_bug.h>
> +
> +#include "debug.h"
> +#include "event.h"
> +#include "evsel.h"
> +#include "dso.h"
> +#include "map.h"
> +#include "thread.h"
> +#include "symbol.h"
> +#include "dlfilter.h"
> +#include "perf_dlfilter.h"
> +
> +static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
> +{
> +	struct symbol *sym = al->sym;
> +
> +	d_al->size = sizeof(*d_al);
> +	if (al->map) {
> +		struct dso *dso = al->map->dso;
> +
> +		if (symbol_conf.show_kernel_path && dso->long_name)
> +			d_al->dso = dso->long_name;
> +		else
> +			d_al->dso = dso->name;
> +		d_al->is_64_bit = dso->is_64_bit;
> +		d_al->buildid_size = dso->bid.size;
> +		d_al->buildid = dso->bid.data;
> +	} else {
> +		d_al->dso = NULL;
> +		d_al->is_64_bit = 0;
> +		d_al->buildid_size = 0;
> +		d_al->buildid = NULL;
> +	}
> +	if (sym) {
> +		d_al->sym = sym->name;
> +		d_al->sym_start = sym->start;
> +		d_al->sym_end = sym->end;
> +		if (al->addr < sym->end)
> +			d_al->symoff = al->addr - sym->start;
> +		else
> +			d_al->symoff = al->addr - al->map->start - sym->start;
> +		d_al->sym_binding = sym->binding;
> +	} else {
> +		d_al->sym = NULL;
> +		d_al->sym_start = 0;
> +		d_al->sym_end = 0;
> +		d_al->symoff = 0;
> +		d_al->sym_binding = 0;
> +	}
> +	d_al->addr = al->addr;
> +	d_al->comm = NULL;
> +	d_al->filtered = 0;
> +}
> +
> +static struct addr_location *get_al(struct dlfilter *d)
> +{
> +	struct addr_location *al = d->al;
> +
> +	if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
> +		return NULL;
> +	return al;
> +}
> +
> +static struct thread *get_thread(struct dlfilter *d)
> +{
> +	struct addr_location *al = get_al(d);
> +
> +	return al ? al->thread : NULL;
> +}
> +
> +static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
> +{
> +	struct dlfilter *d = (struct dlfilter *)ctx;
> +	struct perf_dlfilter_al *d_al = d->d_ip_al;
> +	struct addr_location *al;
> +
> +	if (!d->ctx_valid)
> +		return NULL;
> +
> +	/* 'size' is also used to indicate already initialized */
> +	if (d_al->size)
> +		return d_al;
> +
> +	al = get_al(d);
> +	if (!al)
> +		return NULL;
> +
> +	al_to_d_al(al, d_al);
> +
> +	d_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->ip);
> +	d_al->comm = al->thread ? thread__comm_str(al->thread) : ":-1";
> +	d_al->filtered = al->filtered;
> +
> +	return d_al;
> +}
> +
> +static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
> +{
> +	struct dlfilter *d = (struct dlfilter *)ctx;
> +	struct perf_dlfilter_al *d_addr_al = d->d_addr_al;
> +	struct addr_location *addr_al = d->addr_al;
> +
> +	if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
> +		return NULL;
> +
> +	/* 'size' is also used to indicate already initialized */
> +	if (d_addr_al->size)
> +		return d_addr_al;
> +
> +	if (!addr_al->thread) {
> +		struct thread *thread = get_thread(d);
> +
> +		if (!thread)
> +			return NULL;
> +		thread__resolve(thread, addr_al, d->sample);
> +	}
> +
> +	al_to_d_al(addr_al, d_addr_al);
> +
> +	d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
> +
> +	return d_addr_al;
> +}
> +
> +static const struct perf_dlfilter_fns perf_dlfilter_fns = {
> +	.resolve_ip      = dlfilter__resolve_ip,
> +	.resolve_addr    = dlfilter__resolve_addr,
> +};
> +
> +#define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
> +
> +static int dlfilter__init(struct dlfilter *d, const char *file)
> +{
> +	CHECK_FLAG(BRANCH);
> +	CHECK_FLAG(CALL);
> +	CHECK_FLAG(RETURN);
> +	CHECK_FLAG(CONDITIONAL);
> +	CHECK_FLAG(SYSCALLRET);
> +	CHECK_FLAG(ASYNC);
> +	CHECK_FLAG(INTERRUPT);
> +	CHECK_FLAG(TX_ABORT);
> +	CHECK_FLAG(TRACE_BEGIN);
> +	CHECK_FLAG(TRACE_END);
> +	CHECK_FLAG(IN_TX);
> +	CHECK_FLAG(VMENTRY);
> +	CHECK_FLAG(VMEXIT);
> +
> +	memset(d, 0, sizeof(*d));
> +	d->file = strdup(file);
> +	if (!d->file)
> +		return -1;
> +	return 0;
> +}
> +
> +static void dlfilter__exit(struct dlfilter *d)
> +{
> +	zfree(&d->file);
> +}
> +
> +static int dlfilter__open(struct dlfilter *d)
> +{
> +	d->handle = dlopen(d->file, RTLD_NOW);
> +	if (!d->handle) {
> +		pr_err("dlopen failed for: '%s'\n", d->file);
> +		return -1;
> +	}
> +	d->start = dlsym(d->handle, "start");
> +	d->filter_event = dlsym(d->handle, "filter_event");
> +	d->stop = dlsym(d->handle, "stop");
> +	d->fns = dlsym(d->handle, "perf_dlfilter_fns");
> +	if (d->fns)
> +		memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
> +	return 0;
> +}
> +
> +static int dlfilter__close(struct dlfilter *d)
> +{
> +	return dlclose(d->handle);
> +}
> +
> +struct dlfilter *dlfilter__new(const char *file)
> +{
> +	struct dlfilter *d = malloc(sizeof(*d));
> +
> +	if (!d)
> +		return NULL;
> +
> +	if (dlfilter__init(d, file))
> +		goto err_free;
> +
> +	if (dlfilter__open(d))
> +		goto err_exit;
> +
> +	return d;
> +
> +err_exit:
> +	dlfilter__exit(d);
> +err_free:
> +	free(d);
> +	return NULL;
> +}
> +
> +static void dlfilter__free(struct dlfilter *d)
> +{
> +	if (d) {
> +		dlfilter__exit(d);
> +		free(d);
> +	}
> +}
> +
> +int dlfilter__start(struct dlfilter *d, struct perf_session *session)
> +{
> +	if (d) {
> +		d->session = session;
> +		if (d->start)
> +			return d->start(&d->data);
> +	}
> +	return 0;
> +}
> +
> +static int dlfilter__stop(struct dlfilter *d)
> +{
> +	if (d && d->stop)
> +		return d->stop(d->data);
> +	return 0;
> +}
> +
> +void dlfilter__cleanup(struct dlfilter *d)
> +{
> +	if (d) {
> +		dlfilter__stop(d);
> +		dlfilter__close(d);
> +		dlfilter__free(d);
> +	}
> +}
> +
> +#define ASSIGN(x) d_sample.x = sample->x
> +
> +int dlfilter__do_filter_event(struct dlfilter *d,
> +			      union perf_event *event,
> +			      struct perf_sample *sample,
> +			      struct evsel *evsel,
> +			      struct machine *machine,
> +			      struct addr_location *al,
> +			      struct addr_location *addr_al)
> +{
> +	struct perf_dlfilter_sample d_sample;
> +	struct perf_dlfilter_al d_ip_al;
> +	struct perf_dlfilter_al d_addr_al;
> +	int ret;
> +
> +	d->event       = event;
> +	d->sample      = sample;
> +	d->evsel       = evsel;
> +	d->machine     = machine;
> +	d->al          = al;
> +	d->addr_al     = addr_al;
> +	d->d_sample    = &d_sample;
> +	d->d_ip_al     = &d_ip_al;
> +	d->d_addr_al   = &d_addr_al;
> +
> +	d_sample.size  = sizeof(d_sample);
> +	d_ip_al.size   = 0; /* To indicate d_ip_al is not initialized */
> +	d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
> +
> +	ASSIGN(ip);
> +	ASSIGN(pid);
> +	ASSIGN(tid);
> +	ASSIGN(time);
> +	ASSIGN(addr);
> +	ASSIGN(id);
> +	ASSIGN(stream_id);
> +	ASSIGN(period);
> +	ASSIGN(weight);
> +	ASSIGN(ins_lat);
> +	ASSIGN(p_stage_cyc);
> +	ASSIGN(transaction);
> +	ASSIGN(insn_cnt);
> +	ASSIGN(cyc_cnt);
> +	ASSIGN(cpu);
> +	ASSIGN(flags);
> +	ASSIGN(data_src);
> +	ASSIGN(phys_addr);
> +	ASSIGN(data_page_size);
> +	ASSIGN(code_page_size);
> +	ASSIGN(cgroup);
> +	ASSIGN(cpumode);
> +	ASSIGN(misc);
> +	ASSIGN(raw_size);
> +	ASSIGN(raw_data);
> +
> +	if (sample->branch_stack) {
> +		d_sample.brstack_nr = sample->branch_stack->nr;
> +		d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
> +	} else {
> +		d_sample.brstack_nr = 0;
> +		d_sample.brstack = NULL;
> +	}
> +
> +	if (sample->callchain) {
> +		d_sample.raw_callchain_nr = sample->callchain->nr;
> +		d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
> +	} else {
> +		d_sample.raw_callchain_nr = 0;
> +		d_sample.raw_callchain = NULL;
> +	}
> +
> +	d_sample.addr_correlates_sym =
> +		(evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
> +		sample_addr_correlates_sym(&evsel->core.attr);
> +
> +	d_sample.event = evsel__name(evsel);
> +
> +	d->ctx_valid = true;
> +
> +	ret = d->filter_event(d->data, &d_sample, d);
> +
> +	d->ctx_valid = false;
> +
> +	return ret;
> +}
> diff --git a/tools/perf/util/dlfilter.h b/tools/perf/util/dlfilter.h
> new file mode 100644
> index 000000000000..671e2d3d5a06
> --- /dev/null
> +++ b/tools/perf/util/dlfilter.h
> @@ -0,0 +1,74 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * dlfilter.h: Interface to perf script --dlfilter shared object
> + * Copyright (c) 2021, Intel Corporation.
> + */
> +
> +#ifndef PERF_UTIL_DLFILTER_H
> +#define PERF_UTIL_DLFILTER_H
> +
> +struct perf_session;
> +union  perf_event;
> +struct perf_sample;
> +struct evsel;
> +struct machine;
> +struct addr_location;
> +struct perf_dlfilter_fns;
> +struct perf_dlfilter_sample;
> +struct perf_dlfilter_al;
> +
> +struct dlfilter {
> +	char				*file;
> +	void				*handle;
> +	void				*data;
> +	struct perf_session		*session;
> +	bool				ctx_valid;
> +
> +	union perf_event		*event;
> +	struct perf_sample		*sample;
> +	struct evsel			*evsel;
> +	struct machine			*machine;
> +	struct addr_location		*al;
> +	struct addr_location		*addr_al;
> +	struct perf_dlfilter_sample	*d_sample;
> +	struct perf_dlfilter_al		*d_ip_al;
> +	struct perf_dlfilter_al		*d_addr_al;
> +
> +	int (*start)(void **data);
> +	int (*stop)(void *data);
> +
> +	int (*filter_event)(void *data,
> +			    const struct perf_dlfilter_sample *sample,
> +			    void *ctx);
> +
> +	struct perf_dlfilter_fns *fns;
> +};
> +
> +struct dlfilter *dlfilter__new(const char *file);
> +
> +int dlfilter__start(struct dlfilter *d, struct perf_session *session);
> +
> +int dlfilter__do_filter_event(struct dlfilter *d,
> +			      union perf_event *event,
> +			      struct perf_sample *sample,
> +			      struct evsel *evsel,
> +			      struct machine *machine,
> +			      struct addr_location *al,
> +			      struct addr_location *addr_al);
> +
> +void dlfilter__cleanup(struct dlfilter *d);
> +
> +static inline int dlfilter__filter_event(struct dlfilter *d,
> +					 union perf_event *event,
> +					 struct perf_sample *sample,
> +					 struct evsel *evsel,
> +					 struct machine *machine,
> +					 struct addr_location *al,
> +					 struct addr_location *addr_al)
> +{
> +	if (!d || !d->filter_event)
> +		return 0;
> +	return dlfilter__do_filter_event(d, event, sample, evsel, machine, al, addr_al);
> +}
> +
> +#endif
> diff --git a/tools/perf/util/perf_dlfilter.h b/tools/perf/util/perf_dlfilter.h
> new file mode 100644
> index 000000000000..132f833f0a0b
> --- /dev/null
> +++ b/tools/perf/util/perf_dlfilter.h
> @@ -0,0 +1,120 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * perf_dlfilter.h: API for perf --dlfilter shared object
> + * Copyright (c) 2021, Intel Corporation.
> + */
> +#ifndef _LINUX_PERF_DLFILTER_H
> +#define _LINUX_PERF_DLFILTER_H
> +
> +#include <linux/perf_event.h>
> +#include <linux/types.h>
> +
> +/* Definitions for perf_dlfilter_sample flags */
> +enum {
> +	PERF_DLFILTER_FLAG_BRANCH	= 1ULL << 0,
> +	PERF_DLFILTER_FLAG_CALL		= 1ULL << 1,
> +	PERF_DLFILTER_FLAG_RETURN	= 1ULL << 2,
> +	PERF_DLFILTER_FLAG_CONDITIONAL	= 1ULL << 3,
> +	PERF_DLFILTER_FLAG_SYSCALLRET	= 1ULL << 4,
> +	PERF_DLFILTER_FLAG_ASYNC	= 1ULL << 5,
> +	PERF_DLFILTER_FLAG_INTERRUPT	= 1ULL << 6,
> +	PERF_DLFILTER_FLAG_TX_ABORT	= 1ULL << 7,
> +	PERF_DLFILTER_FLAG_TRACE_BEGIN	= 1ULL << 8,
> +	PERF_DLFILTER_FLAG_TRACE_END	= 1ULL << 9,
> +	PERF_DLFILTER_FLAG_IN_TX	= 1ULL << 10,
> +	PERF_DLFILTER_FLAG_VMENTRY	= 1ULL << 11,
> +	PERF_DLFILTER_FLAG_VMEXIT	= 1ULL << 12,
> +};
> +
> +/*
> + * perf sample event information (as per perf script and <linux/perf_event.h>)
> + */
> +struct perf_dlfilter_sample {
> +	__u32 size; /* Size of this structure (for compatibility checking) */
> +	__u64 ip;
> +	__s32 pid;
> +	__s32 tid;
> +	__u64 time;
> +	__u64 addr;
> +	__u64 id;
> +	__u64 stream_id;
> +	__u64 period;
> +	__u64 weight;		/* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
> +	__u16 ins_lat;		/* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
> +	__u16 p_stage_cyc;	/* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */
> +	__u64 transaction;	/* Refer PERF_SAMPLE_TRANSACTION in <linux/perf_event.h> */
> +	__u64 insn_cnt;	/* For instructions-per-cycle (IPC) */
> +	__u64 cyc_cnt;		/* For instructions-per-cycle (IPC) */
> +	__s32 cpu;
> +	__u32 flags;		/* Refer PERF_DLFILTER_FLAG_* above */
> +	__u64 data_src;		/* Refer PERF_SAMPLE_DATA_SRC in <linux/perf_event.h> */
> +	__u64 phys_addr;	/* Refer PERF_SAMPLE_PHYS_ADDR in <linux/perf_event.h> */
> +	__u64 data_page_size;	/* Refer PERF_SAMPLE_DATA_PAGE_SIZE in <linux/perf_event.h> */
> +	__u64 code_page_size;	/* Refer PERF_SAMPLE_CODE_PAGE_SIZE in <linux/perf_event.h> */
> +	__u64 cgroup;		/* Refer PERF_SAMPLE_CGROUP in <linux/perf_event.h> */
> +	__u8  cpumode;		/* Refer CPUMODE_MASK etc in <linux/perf_event.h> */
> +	__u8  addr_correlates_sym; /* True => resolve_addr() can be called */
> +	__u16 misc;		/* Refer perf_event_header in <linux/perf_event.h> */
> +	__u32 raw_size;		/* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */
> +	const void *raw_data;	/* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */
> +	__u64 brstack_nr;	/* Number of brstack entries */
> +	const struct perf_branch_entry *brstack; /* Refer <linux/perf_event.h> */
> +	__u64 raw_callchain_nr;	/* Number of raw_callchain entries */
> +	const __u64 *raw_callchain; /* Refer <linux/perf_event.h> */
> +	const char *event;
> +};
> +
> +/*
> + * Address location (as per perf script)
> + */
> +struct perf_dlfilter_al {
> +	__u32 size; /* Size of this structure (for compatibility checking) */
> +	__u32 symoff;
> +	const char *sym;
> +	__u64 addr; /* Mapped address (from dso) */
> +	__u64 sym_start;
> +	__u64 sym_end;
> +	const char *dso;
> +	__u8  sym_binding; /* STB_LOCAL, STB_GLOBAL or STB_WEAK, refer <elf.h> */
> +	__u8  is_64_bit; /* Only valid if dso is not NULL */
> +	__u8  is_kernel_ip; /* True if in kernel space */
> +	__u32 buildid_size;
> +	__u8 *buildid;
> +	/* Below members are only populated by resolve_ip() */
> +	__u8 filtered; /* True if this sample event will be filtered out */
> +	const char *comm;
> +};
> +
> +struct perf_dlfilter_fns {
> +	/* Return information about ip */
> +	const struct perf_dlfilter_al *(*resolve_ip)(void *ctx);
> +	/* Return information about addr (if addr_correlates_sym) */
> +	const struct perf_dlfilter_al *(*resolve_addr)(void *ctx);
> +	/* Reserved */
> +	void *(*reserved[126])(void *);
> +};
> +
> +/*
> + * If implemented, 'start' will be called at the beginning,
> + * before any calls to 'filter_event'. Return 0 to indicate success,
> + * or return a negative error code. '*data' can be assigned for use
> + * by other functions.
> + */
> +int start(void **data);
> +
> +/*
> + * If implemented, 'stop' will be called at the end,
> + * after any calls to 'filter_event'. Return 0 to indicate success, or
> + * return a negative error code. 'data' is set by start().
> + */
> +int stop(void *data);
> +
> +/*
> + * If implemented, 'filter_event' will be called for each sample
> + * event. Return 0 to keep the sample event, 1 to filter it out, or
> + * return a negative error code. 'data' is set by start(). 'ctx' is
> + * needed for calls to perf_dlfilter_fns.
> + */
> +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx);
> +
> +#endif
> -- 
> 2.17.1
> 

-- 

- Arnaldo

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ