Commit 246eba8e authored by Adrian Hunter's avatar Adrian Hunter Committed by Arnaldo Carvalho de Melo
Browse files

perf tools: Add support for PERF_RECORD_TEXT_POKE



Add processing for PERF_RECORD_TEXT_POKE events. When a text poke event
is processed, then the kernel dso data cache is updated with the poked
bytes.

Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: x86@kernel.org
Link: http://lore.kernel.org/lkml/20200512121922.8997-12-adrian.hunter@intel.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b39730a6
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -383,7 +383,8 @@ struct perf_event_attr {
				bpf_event      :  1, /* include bpf events */
				aux_output     :  1, /* generate AUX records instead of events */
				cgroup         :  1, /* include cgroup events */
				__reserved_1   : 31;
				text_poke      :  1, /* include text poke events */
				__reserved_1   : 30;

	union {
		__u32		wakeup_events;	  /* wakeup every n events */
@@ -1024,6 +1025,24 @@ enum perf_event_type {
	 */
	PERF_RECORD_CGROUP			= 19,

	/*
	 * Records changes to kernel text i.e. self-modified code. 'old_len' is
	 * the number of old bytes, 'new_len' is the number of new bytes. Either
	 * 'old_len' or 'new_len' may be zero to indicate, for example, the
	 * addition or removal of a trampoline. 'bytes' contains the old bytes
	 * followed immediately by the new bytes.
	 *
	 * struct {
	 *	struct perf_event_header	header;
	 *	u64				addr;
	 *	u16				old_len;
	 *	u16				new_len;
	 *	u8				bytes[];
	 *	struct sample_id		sample_id;
	 * };
	 */
	PERF_RECORD_TEXT_POKE			= 20,

	PERF_RECORD_MAX,			/* non-ABI */
};

+9 −0
Original line number Diff line number Diff line
@@ -111,6 +111,14 @@ struct perf_record_cgroup {
	char			 path[PATH_MAX];
};

struct perf_record_text_poke_event {
	struct perf_event_header header;
	__u64			addr;
	__u16			old_len;
	__u16			new_len;
	__u8			bytes[];
};

struct perf_record_sample {
	struct perf_event_header header;
	__u64			 array[];
@@ -367,6 +375,7 @@ union perf_event {
	struct perf_record_sample		sample;
	struct perf_record_bpf_event		bpf;
	struct perf_record_ksymbol		ksymbol;
	struct perf_record_text_poke_event	text_poke;
	struct perf_record_header_attr		attr;
	struct perf_record_event_update		event_update;
	struct perf_record_header_event_type	event_type;
+45 −0
Original line number Diff line number Diff line
@@ -765,6 +765,43 @@ static int record__auxtrace_init(struct record *rec __maybe_unused)

#endif

static int record__config_text_poke(struct evlist *evlist)
{
	struct evsel *evsel;
	int err;

	/* Nothing to do if text poke is already configured */
	evlist__for_each_entry(evlist, evsel) {
		if (evsel->core.attr.text_poke)
			return 0;
	}

	err = parse_events(evlist, "dummy:u", NULL);
	if (err)
		return err;

	evsel = evlist__last(evlist);

	evsel->core.attr.freq = 0;
	evsel->core.attr.sample_period = 1;
	evsel->core.attr.text_poke = 1;
	evsel->core.attr.ksymbol = 1;

	evsel->core.system_wide = true;
	evsel->no_aux_samples = true;
	evsel->immediate = true;

	/* Text poke must be collected on all CPUs */
	perf_cpu_map__put(evsel->core.own_cpus);
	evsel->core.own_cpus = perf_cpu_map__new(NULL);
	perf_cpu_map__put(evsel->core.cpus);
	evsel->core.cpus = perf_cpu_map__get(evsel->core.own_cpus);

	evsel__set_sample_bit(evsel, TIME);

	return 0;
}

static bool record__kcore_readable(struct machine *machine)
{
	char kcore[PATH_MAX];
@@ -2766,6 +2803,14 @@ int cmd_record(int argc, const char **argv)
	if (rec->opts.full_auxtrace)
		rec->buildid_all = true;

	if (rec->opts.text_poke) {
		err = record__config_text_poke(rec->evlist);
		if (err) {
			pr_err("record__config_text_poke failed, error %d\n", err);
			goto out;
		}
	}

	if (record_opts__config(&rec->opts)) {
		err = -EINVAL;
		goto out;
+47 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include "stat.h"
#include "session.h"
#include "bpf-event.h"
#include "print_binary.h"
#include "tool.h"
#include "../perf.h"

@@ -55,6 +56,7 @@ static const char *perf_event__names[] = {
	[PERF_RECORD_KSYMBOL]			= "KSYMBOL",
	[PERF_RECORD_BPF_EVENT]			= "BPF_EVENT",
	[PERF_RECORD_CGROUP]			= "CGROUP",
	[PERF_RECORD_TEXT_POKE]			= "TEXT_POKE",
	[PERF_RECORD_HEADER_ATTR]		= "ATTR",
	[PERF_RECORD_HEADER_EVENT_TYPE]		= "EVENT_TYPE",
	[PERF_RECORD_HEADER_TRACING_DATA]	= "TRACING_DATA",
@@ -267,6 +269,14 @@ int perf_event__process_bpf(struct perf_tool *tool __maybe_unused,
	return machine__process_bpf(machine, event, sample);
}

int perf_event__process_text_poke(struct perf_tool *tool __maybe_unused,
				  union perf_event *event,
				  struct perf_sample *sample,
				  struct machine *machine)
{
	return machine__process_text_poke(machine, event, sample);
}

size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
{
	return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 "]: %c %s\n",
@@ -413,6 +423,40 @@ size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp)
		       event->bpf.type, event->bpf.flags, event->bpf.id);
}

static int text_poke_printer(enum binary_printer_ops op, unsigned int val,
			     void *extra, FILE *fp)
{
	bool old = *(bool *)extra;

	switch ((int)op) {
	case BINARY_PRINT_LINE_BEGIN:
		return fprintf(fp, "            %s bytes:", old ? "Old" : "New");
	case BINARY_PRINT_NUM_DATA:
		return fprintf(fp, " %02x", val);
	case BINARY_PRINT_LINE_END:
		return fprintf(fp, "\n");
	default:
		return 0;
	}
}

size_t perf_event__fprintf_text_poke(union perf_event *event, FILE *fp)
{
	struct perf_record_text_poke_event *tp = &event->text_poke;
	size_t ret;
	bool old;

	ret = fprintf(fp, " %" PRI_lx64 " old len %u new len %u\n",
		      tp->addr, tp->old_len, tp->new_len);
	old = true;
	ret += binary__fprintf(tp->bytes, tp->old_len, 16, text_poke_printer,
			       &old, fp);
	old = false;
	ret += binary__fprintf(tp->bytes + tp->old_len, tp->new_len, 16,
			       text_poke_printer, &old, fp);
	return ret;
}

size_t perf_event__fprintf(union perf_event *event, FILE *fp)
{
	size_t ret = fprintf(fp, "PERF_RECORD_%s",
@@ -457,6 +501,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
	case PERF_RECORD_BPF_EVENT:
		ret += perf_event__fprintf_bpf(event, fp);
		break;
	case PERF_RECORD_TEXT_POKE:
		ret += perf_event__fprintf_text_poke(event, fp);
		break;
	default:
		ret += fprintf(fp, "\n");
	}
+5 −0
Original line number Diff line number Diff line
@@ -351,6 +351,10 @@ int perf_event__process_bpf(struct perf_tool *tool,
			    union perf_event *event,
			    struct perf_sample *sample,
			    struct machine *machine);
int perf_event__process_text_poke(struct perf_tool *tool,
				  union perf_event *event,
				  struct perf_sample *sample,
				  struct machine *machine);
int perf_event__process(struct perf_tool *tool,
			union perf_event *event,
			struct perf_sample *sample,
@@ -385,6 +389,7 @@ size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_cgroup(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_text_poke(union perf_event *event, FILE *fp);
size_t perf_event__fprintf(union perf_event *event, FILE *fp);

int kallsyms__get_function_start(const char *kallsyms_filename,
Loading