From 246eba8e9041c4774738645c72b6efac7601db22 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 12 May 2020 15:19:18 +0300 Subject: 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: Adrian Hunter Cc: Alexander Shishkin Cc: Borislav Petkov Cc: H. Peter Anvin Cc: Jiri Olsa Cc: Leo Yan Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Mathieu Poirier Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Cc: x86@kernel.org Link: http://lore.kernel.org/lkml/20200512121922.8997-12-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 47 +++++++++++++++++++++++++++++++ tools/perf/util/event.h | 5 ++++ tools/perf/util/evsel.c | 7 ++++- tools/perf/util/machine.c | 43 ++++++++++++++++++++++++++++ tools/perf/util/machine.h | 3 ++ tools/perf/util/perf_api_probe.c | 10 +++++++ tools/perf/util/perf_api_probe.h | 1 + tools/perf/util/perf_event_attr_fprintf.c | 1 + tools/perf/util/record.h | 1 + tools/perf/util/session.c | 23 +++++++++++++++ tools/perf/util/tool.h | 3 +- 11 files changed, 142 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index f581550a3015..dde2f0147713 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -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"); } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6ae01c3c2ffa..3c4eb94f93de 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -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, diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ef802f6d40c1..9aa51a65593d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1064,7 +1064,12 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, attr->mmap = track; attr->mmap2 = track && !perf_missing_features.mmap2; attr->comm = track; - attr->ksymbol = track && !perf_missing_features.ksymbol; + /* + * ksymbol is tracked separately with text poke because it needs to be + * system wide and enabled immediately. + */ + if (!opts->text_poke) + attr->ksymbol = track && !perf_missing_features.ksymbol; attr->bpf_event = track && !opts->no_bpf_event && !perf_missing_features.bpf; if (opts->record_namespaces) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index d5384807372b..1e9fe6b4d8db 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -808,6 +808,47 @@ int machine__process_ksymbol(struct machine *machine __maybe_unused, return machine__process_ksymbol_register(machine, event, sample); } +int machine__process_text_poke(struct machine *machine, union perf_event *event, + struct perf_sample *sample __maybe_unused) +{ + struct map *map = maps__find(&machine->kmaps, event->text_poke.addr); + u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + if (dump_trace) + perf_event__fprintf_text_poke(event, stdout); + + if (!event->text_poke.new_len) + return 0; + + if (cpumode != PERF_RECORD_MISC_KERNEL) { + pr_debug("%s: unsupported cpumode - ignoring\n", __func__); + return 0; + } + + if (map && map->dso) { + u8 *new_bytes = event->text_poke.bytes + event->text_poke.old_len; + int ret; + + /* + * Kernel maps might be changed when loading symbols so loading + * must be done prior to using kernel maps. + */ + map__load(map); + ret = dso__data_write_cache_addr(map->dso, map, machine, + event->text_poke.addr, + new_bytes, + event->text_poke.new_len); + if (ret != event->text_poke.new_len) + pr_debug("Failed to write kernel text poke at %#" PRI_lx64 "\n", + event->text_poke.addr); + } else { + pr_debug("Failed to find kernel text poke address map for %#" PRI_lx64 "\n", + event->text_poke.addr); + } + + return 0; +} + static struct map *machine__addnew_module_map(struct machine *machine, u64 start, const char *filename) { @@ -1930,6 +1971,8 @@ int machine__process_event(struct machine *machine, union perf_event *event, ret = machine__process_ksymbol(machine, event, sample); break; case PERF_RECORD_BPF_EVENT: ret = machine__process_bpf(machine, event, sample); break; + case PERF_RECORD_TEXT_POKE: + ret = machine__process_text_poke(machine, event, sample); break; default: ret = -1; break; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index fa1be9ea00fa..062c36a8433c 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -138,6 +138,9 @@ int machine__process_mmap2_event(struct machine *machine, union perf_event *even int machine__process_ksymbol(struct machine *machine, union perf_event *event, struct perf_sample *sample); +int machine__process_text_poke(struct machine *machine, + union perf_event *event, + struct perf_sample *sample); int machine__process_event(struct machine *machine, union perf_event *event, struct perf_sample *sample); diff --git a/tools/perf/util/perf_api_probe.c b/tools/perf/util/perf_api_probe.c index 1337965673d7..3840d02f0f7b 100644 --- a/tools/perf/util/perf_api_probe.c +++ b/tools/perf/util/perf_api_probe.c @@ -93,6 +93,11 @@ static void perf_probe_context_switch(struct evsel *evsel) evsel->core.attr.context_switch = 1; } +static void perf_probe_text_poke(struct evsel *evsel) +{ + evsel->core.attr.text_poke = 1; +} + bool perf_can_sample_identifier(void) { return perf_probe_api(perf_probe_sample_identifier); @@ -108,6 +113,11 @@ bool perf_can_record_switch_events(void) return perf_probe_api(perf_probe_context_switch); } +bool perf_can_record_text_poke_events(void) +{ + return perf_probe_api(perf_probe_text_poke); +} + bool perf_can_record_cpu_wide(void) { struct perf_event_attr attr = { diff --git a/tools/perf/util/perf_api_probe.h b/tools/perf/util/perf_api_probe.h index 706c3c6426e2..d5506a983a94 100644 --- a/tools/perf/util/perf_api_probe.h +++ b/tools/perf/util/perf_api_probe.h @@ -9,6 +9,7 @@ bool perf_can_aux_sample(void); bool perf_can_comm_exec(void); bool perf_can_record_cpu_wide(void); bool perf_can_record_switch_events(void); +bool perf_can_record_text_poke_events(void); bool perf_can_sample_identifier(void); #endif // __PERF_API_PROBE_H diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c index b94fa07f5d32..e67a227c0ce7 100644 --- a/tools/perf/util/perf_event_attr_fprintf.c +++ b/tools/perf/util/perf_event_attr_fprintf.c @@ -147,6 +147,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, PRINT_ATTRf(aux_watermark, p_unsigned); PRINT_ATTRf(sample_max_stack, p_unsigned); PRINT_ATTRf(aux_sample_size, p_unsigned); + PRINT_ATTRf(text_poke, p_unsigned); return ret; } diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h index 39d1de4b2a36..84dbbc3f0204 100644 --- a/tools/perf/util/record.h +++ b/tools/perf/util/record.h @@ -48,6 +48,7 @@ struct record_opts { bool sample_id; bool no_bpf_event; bool kcore; + bool text_poke; unsigned int freq; unsigned int mmap_pages; unsigned int auxtrace_mmap_pages; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 396424fcaadf..ffbc9d35a383 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -490,6 +490,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->ksymbol = perf_event__process_ksymbol; if (tool->bpf == NULL) tool->bpf = perf_event__process_bpf; + if (tool->text_poke == NULL) + tool->text_poke = perf_event__process_text_poke; if (tool->read == NULL) tool->read = process_event_sample_stub; if (tool->throttle == NULL) @@ -659,6 +661,24 @@ static void perf_event__switch_swap(union perf_event *event, bool sample_id_all) swap_sample_id_all(event, &event->context_switch + 1); } +static void perf_event__text_poke_swap(union perf_event *event, bool sample_id_all) +{ + event->text_poke.addr = bswap_64(event->text_poke.addr); + event->text_poke.old_len = bswap_16(event->text_poke.old_len); + event->text_poke.new_len = bswap_16(event->text_poke.new_len); + + if (sample_id_all) { + size_t len = sizeof(event->text_poke.old_len) + + sizeof(event->text_poke.new_len) + + event->text_poke.old_len + + event->text_poke.new_len; + void *data = &event->text_poke.old_len; + + data += PERF_ALIGN(len, sizeof(u64)); + swap_sample_id_all(event, data); + } +} + static void perf_event__throttle_swap(union perf_event *event, bool sample_id_all) { @@ -932,6 +952,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_SWITCH] = perf_event__switch_swap, [PERF_RECORD_SWITCH_CPU_WIDE] = perf_event__switch_swap, [PERF_RECORD_NAMESPACES] = perf_event__namespaces_swap, + [PERF_RECORD_TEXT_POKE] = perf_event__text_poke_swap, [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, @@ -1474,6 +1495,8 @@ static int machines__deliver_event(struct machines *machines, return tool->ksymbol(tool, event, sample, machine); case PERF_RECORD_BPF_EVENT: return tool->bpf(tool, event, sample, machine); + case PERF_RECORD_TEXT_POKE: + return tool->text_poke(tool, event, sample, machine); default: ++evlist->stats.nr_unknown_events; return -1; diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 3fb67bd31e4a..bbbc0dcd461f 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -57,7 +57,8 @@ struct perf_tool { throttle, unthrottle, ksymbol, - bpf; + bpf, + text_poke; event_attr_op attr; event_attr_op event_update; -- cgit v1.2.3