lkml.org 
[lkml]   [2020]   [May]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH V7 11/15] perf tools: Add support for PERF_RECORD_TEXT_POKE
    Date
    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 <adrian.hunter@intel.com>
    ---
    tools/include/uapi/linux/perf_event.h | 21 +++++++++-
    tools/lib/perf/include/perf/event.h | 9 +++++
    tools/perf/builtin-record.c | 45 ++++++++++++++++++++++
    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 +-
    14 files changed, 216 insertions(+), 3 deletions(-)

    diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
    index 7b2d6fc9e6ed..e5bee6c17b86 100644
    --- a/tools/include/uapi/linux/perf_event.h
    +++ b/tools/include/uapi/linux/perf_event.h
    @@ -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 */
    };

    diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
    index 69b44d2cc0f5..842028858d66 100644
    --- a/tools/lib/perf/include/perf/event.h
    +++ b/tools/lib/perf/include/perf/event.h
    @@ -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;
    diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
    index ae9430b4cdfe..02d85f4e4517 100644
    --- a/tools/perf/builtin-record.c
    +++ b/tools/perf/builtin-record.c
    @@ -743,6 +743,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];
    @@ -2712,6 +2749,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;
    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 b8289f160f07..dbbba6dea51d 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 28683b0eb738..f2edcb2ff68b 100644
    --- a/tools/perf/util/evsel.c
    +++ b/tools/perf/util/evsel.c
    @@ -1069,7 +1069,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 3dc88655e639..c7282c01ee53 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 923565c3b155..dce6332f5071 100644
    --- a/tools/perf/util/record.h
    +++ b/tools/perf/util/record.h
    @@ -47,6 +47,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 b860f9f1b09e..652dd58a649f 100644
    --- a/tools/perf/util/session.c
    +++ b/tools/perf/util/session.c
    @@ -491,6 +491,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)
    @@ -660,6 +662,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)
    {
    @@ -933,6 +953,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,
    @@ -1475,6 +1496,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;
    --
    2.17.1
    \
     
     \ /
      Last update: 2020-05-12 14:20    [W:4.314 / U:0.476 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site