Em Fri, Jan 27, 2017 at 12:43:05PM -0300, Arnaldo Carvalho de Melo escreveu: > Em Fri, Jan 27, 2017 at 02:07:02PM +0100, Peter Zijlstra escreveu: > > Something like the (compile tested only) below might be sufficient to > > disambiguate things. It would need a corresponding tools/perf patch of > > course, but I'm not too familiar with that code anymore. > > I'm working on patch to do feature test, fallback and handling of the > event, etc, will post later.
Just compile tested, need to build a kernel with PeterZ's patch to test, feel free to go from there if in a hurry. The place where the map is yanked out of the thread's maps rbtree is at machine__process_munmap_event() The rest is making sure the tool works with older kernels, deals with endianness in the record in a perf.data file for cross platform analysis, hooking it to the various tools where handling this event makes sense. [acme@jouet linux]$ diffstat /tmp/a.patch include/uapi/linux/perf_event.h | 14 +++++++++- perf/builtin-annotate.c | 1 perf/builtin-c2c.c | 1 perf/builtin-diff.c | 1 perf/builtin-inject.c | 17 +++++++++++- perf/builtin-kmem.c | 1 perf/builtin-mem.c | 1 perf/builtin-record.c | 1 perf/builtin-report.c | 1 perf/builtin-script.c | 30 ++++++++++++++++++++++ perf/builtin-trace.c | 1 perf/util/data-convert-bt.c | 1 perf/util/event.c | 18 +++++++++++++ perf/util/event.h | 10 +++++++ perf/util/evsel.c | 12 +++++++- perf/util/machine.c | 54 ++++++++++++++++++++++++++++++++++++++++ perf/util/machine.h | 2 + perf/util/python.c | 1 perf/util/session.c | 19 ++++++++++++++ perf/util/tool.h | 1 20 files changed, 183 insertions(+), 4 deletions(-) [acme@jouet linux]$ diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h index c66a485a24ac..59c5cd5abbbf 100644 --- a/tools/include/uapi/linux/perf_event.h +++ b/tools/include/uapi/linux/perf_event.h @@ -344,7 +344,8 @@ struct perf_event_attr { use_clockid : 1, /* use @clockid for time fields */ context_switch : 1, /* context switch data */ write_backward : 1, /* Write ring buffer from end to beginning */ - __reserved_1 : 36; + munmap : 1, /* include munmap data */ + __reserved_1 : 35; union { __u32 wakeup_events; /* wakeup every n events */ @@ -862,6 +863,17 @@ enum perf_event_type { */ PERF_RECORD_SWITCH_CPU_WIDE = 15, + /* + * struct { + * struct perf_event_header header; + * + * u64 addr; + * u64 len; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MUNMAP = 16, + PERF_RECORD_MAX, /* non-ABI */ }; diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ebb628332a6e..082b5f100ac5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -390,6 +390,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) .sample = process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index e2b21723bbf8..3e1f1eba77e2 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -313,6 +313,7 @@ static struct perf_c2c c2c = { .sample = process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 9ff0db4e2d0c..2c7856848887 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -350,6 +350,7 @@ static struct perf_tool tool = { .sample = diff__process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index b9bc7e39833a..4e6103d0c163 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -282,6 +282,19 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool, return err; } +static int perf_event__repipe_munmap(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + int err; + + err = perf_event__process_munmap(tool, event, sample, machine); + perf_event__repipe(tool, event, sample, machine); + + return err; +} + #ifdef HAVE_JITDUMP static int perf_event__jit_repipe_mmap2(struct perf_tool *tool, union perf_event *event, @@ -569,7 +582,7 @@ static void strip_init(struct perf_inject *inject) static bool has_tracking(struct perf_evsel *evsel) { return evsel->attr.mmap || evsel->attr.mmap2 || evsel->attr.comm || - evsel->attr.task; + evsel->attr.task || evsel->attr.munmap; } #define COMPAT_MASK (PERF_SAMPLE_ID | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | \ @@ -632,6 +645,7 @@ static int __cmd_inject(struct perf_inject *inject) inject->itrace_synth_opts.set) { inject->tool.mmap = perf_event__repipe_mmap; inject->tool.mmap2 = perf_event__repipe_mmap2; + inject->tool.munmap = perf_event__repipe_munmap; inject->tool.fork = perf_event__repipe_fork; inject->tool.tracing_data = perf_event__repipe_tracing_data; } @@ -732,6 +746,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) .sample = perf_event__repipe_sample, .mmap = perf_event__repipe, .mmap2 = perf_event__repipe, + .munmap = perf_event__repipe, .comm = perf_event__repipe, .fork = perf_event__repipe, .exit = perf_event__repipe, diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 29f4751a3574..eb59aa7a0f5b 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -964,6 +964,7 @@ static struct perf_tool perf_kmem = { .comm = perf_event__process_comm, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .ordered_events = true, }; diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index cd7bc4d104e2..de026e9ef1be 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -338,6 +338,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) .sample = process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .lost = perf_event__process_lost, .fork = perf_event__process_fork, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ffac8ca9fb01..f197fc196cd8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1498,6 +1498,7 @@ static struct record record = { .comm = perf_event__process_comm, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .ordered_events = true, }, }; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index dbd7fa028861..96680fbeb664 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -693,6 +693,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) .sample = process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c0783b4f7b6c..27fcbcb15782 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1244,6 +1244,34 @@ static int process_mmap2_event(struct perf_tool *tool, return 0; } +static int process_munmap_event(struct perf_tool *tool, union perf_event *event, + struct perf_sample *sample, struct machine *machine) +{ + struct thread *thread; + struct perf_script *script = container_of(tool, struct perf_script, tool); + struct perf_session *session = script->session; + struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); + + if (perf_event__process_munmap(tool, event, sample, machine) < 0) + return -1; + + thread = machine__findnew_thread(machine, sample->pid, sample->tid); + if (thread == NULL) { + pr_debug("problem processing MUNMAP event, skipping it.\n"); + return -1; + } + + if (!evsel->attr.sample_id_all) { + pr_debug("MUNMAP event requires attr.sample_id_all, skipping it.\n"); + return -1; + } + + print_sample_start(sample, thread, evsel); + perf_event__fprintf(event, stdout); + thread__put(thread); + return 0; +} + static int process_switch_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -1290,6 +1318,7 @@ static int __cmd_script(struct perf_script *script) if (script->show_mmap_events) { script->tool.mmap = process_mmap_event; script->tool.mmap2 = process_mmap2_event; + script->tool.munmap = process_munmap_event; } if (script->show_switch_events) script->tool.context_switch = process_switch_event; @@ -2096,6 +2125,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) .sample = process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 40ef9b293d1b..cfb7e9e2cd7d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2411,6 +2411,7 @@ static int trace__replay(struct trace *trace) trace->tool.sample = trace__process_sample; trace->tool.mmap = perf_event__process_mmap; trace->tool.mmap2 = perf_event__process_mmap2; + trace->tool.munmap = perf_event__process_munmap; trace->tool.comm = perf_event__process_comm; trace->tool.exit = perf_event__process_exit; trace->tool.fork = perf_event__process_fork; diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 4e6cbc99f08e..8d1feb9fe82d 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1462,6 +1462,7 @@ int bt_convert__perf2ctf(const char *input, const char *path, .sample = process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .munmap = perf_event__process_munmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8ab0d7da956b..ffcba9c07c0d 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -18,6 +18,7 @@ static const char *perf_event__names[] = { [0] = "TOTAL", [PERF_RECORD_MMAP] = "MMAP", [PERF_RECORD_MMAP2] = "MMAP2", + [PERF_RECORD_MUNMAP] = "MUNMAP", [PERF_RECORD_LOST] = "LOST", [PERF_RECORD_COMM] = "COMM", [PERF_RECORD_EXIT] = "EXIT", @@ -1080,6 +1081,12 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) event->mmap2.filename); } +size_t perf_event__fprintf_munmap(union perf_event *event, FILE *fp) +{ + return fprintf(fp, " [%#" PRIx64 ", %" PRIu64 "]\n", + event->munmap.start, event->munmap.len); +} + size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp) { struct thread_map *threads = thread_map__new_event(&event->thread_map); @@ -1128,6 +1135,14 @@ int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, return machine__process_mmap2_event(machine, event, sample); } +int perf_event__process_munmap(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + return machine__process_munmap_event(machine, event, sample); +} + size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) { return fprintf(fp, "(%d:%d):(%d:%d)\n", @@ -1199,6 +1214,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) case PERF_RECORD_MMAP2: ret += perf_event__fprintf_mmap2(event, fp); break; + case PERF_RECORD_MUNMAP: + ret += perf_event__fprintf_munmap(event, fp); + break; case PERF_RECORD_AUX: ret += perf_event__fprintf_aux(event, fp); break; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c735c53a26f8..00942b6a9d48 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -33,6 +33,12 @@ struct mmap2_event { char filename[PATH_MAX]; }; +struct munmap_event { + struct perf_event_header header; + u64 start; + u64 len; +}; + struct comm_event { struct perf_event_header header; u32 pid, tid; @@ -484,6 +490,7 @@ union perf_event { struct perf_event_header header; struct mmap_event mmap; struct mmap2_event mmap2; + struct munmap_event munmap; struct comm_event comm; struct fork_event fork; struct lost_event lost; @@ -595,6 +602,8 @@ int perf_event__process_mmap2(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); +int perf_event__process_munmap(struct perf_tool *tool, union perf_event *event, + struct perf_sample *sample, struct machine *machine); int perf_event__process_fork(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -647,6 +656,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_munmap(union perf_event *event, FILE *fp); size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 04e536ae4d88..c4b88e4f2422 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -34,6 +34,7 @@ static struct { bool sample_id_all; bool exclude_guest; bool mmap2; + bool munmap; bool cloexec; bool clockid; bool clockid_wrong; @@ -930,6 +931,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, attr->task = track; attr->mmap = track; attr->mmap2 = track && !perf_missing_features.mmap2; + attr->munmap = track && !perf_missing_features.munmap; attr->comm = track; if (opts->record_switch_events) @@ -1395,6 +1397,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, PRINT_ATTRf(exclude_callchain_kernel, p_unsigned); PRINT_ATTRf(exclude_callchain_user, p_unsigned); PRINT_ATTRf(mmap2, p_unsigned); + PRINT_ATTRf(munmap, p_unsigned); PRINT_ATTRf(comm_exec, p_unsigned); PRINT_ATTRf(use_clockid, p_unsigned); PRINT_ATTRf(context_switch, p_unsigned); @@ -1474,6 +1477,8 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, } fallback_missing_features: + if (perf_missing_features.munmap) + evsel->attr.munmap = 0; if (perf_missing_features.clockid_wrong) evsel->attr.clockid = CLOCK_MONOTONIC; /* should always work */ if (perf_missing_features.clockid) { @@ -1603,10 +1608,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, goto out_close; /* - * Must probe features in the order they were added to the + * Must probe features in the reverse order they were added to the * perf_event_attr interface. */ - if (!perf_missing_features.write_backward && evsel->attr.write_backward) { + if (!perf_missing_features.munmap && evsel->attr.munmap) { + perf_missing_features.munmap = true; + goto fallback_missing_features; + } else if (!perf_missing_features.write_backward && evsel->attr.write_backward) { perf_missing_features.write_backward = true; goto out_close; } else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) { diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 747a034d1ff3..24f2f309d70f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1320,6 +1320,16 @@ static int machine__process_kernel_mmap_event(struct machine *machine, return -1; } +static int machine__process_kernel_munmap_event(struct machine *machine __maybe_unused, + union perf_event *event __maybe_unused) +{ + /* + * XXX: Fill this in as soon as we get munmap event for kernel + * "mmaps", aka module unload + */ + return 0; +} + int machine__process_mmap2_event(struct machine *machine, union perf_event *event, struct perf_sample *sample) @@ -1379,6 +1389,48 @@ int machine__process_mmap2_event(struct machine *machine, return 0; } +int machine__process_munmap_event(struct machine *machine, + union perf_event *event, + struct perf_sample *sample) +{ + struct thread *thread; + struct map *map; + enum map_type type = MAP__FUNCTION; + int ret = 0; + + if (dump_trace) + perf_event__fprintf_munmap(event, stdout); + + if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL || + sample->cpumode == PERF_RECORD_MISC_KERNEL) { + ret = machine__process_kernel_munmap_event(machine, event); + if (ret < 0) + goto out_problem; + return 0; + } + + thread = machine__find_thread(machine, sample->pid, sample->tid); + if (thread == NULL) + goto out_problem; + + if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) + type = MAP__VARIABLE; + + map = map_groups__find(thread->mg, type, event->munmap.start); + if (map != NULL) + goto out_problem_map; + + map_groups__remove(thread->mg, map); + thread__put(thread); + return 0; + +out_problem_map: + thread__put(thread); +out_problem: + dump_printf("problem processing PERF_RECORD_MUNMAP, skipping event.\n"); + return 0; +} + int machine__process_mmap_event(struct machine *machine, union perf_event *event, struct perf_sample *sample) { @@ -1540,6 +1592,8 @@ int machine__process_event(struct machine *machine, union perf_event *event, ret = machine__process_mmap_event(machine, event, sample); break; case PERF_RECORD_MMAP2: ret = machine__process_mmap2_event(machine, event, sample); break; + case PERF_RECORD_MUNMAP: + ret = machine__process_munmap_event(machine, event, sample); break; case PERF_RECORD_FORK: ret = machine__process_fork_event(machine, event, sample); break; case PERF_RECORD_EXIT: diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index a28305029711..eabc8b9d8d36 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -101,6 +101,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event struct perf_sample *sample); int machine__process_mmap2_event(struct machine *machine, union perf_event *event, struct perf_sample *sample); +int machine__process_munmap_event(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/python.c b/tools/perf/util/python.c index a5fbc012e3df..047e004dc53f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1155,6 +1155,7 @@ static struct { PERF_CONST(RECORD_READ), PERF_CONST(RECORD_SAMPLE), PERF_CONST(RECORD_MMAP2), + PERF_CONST(RECORD_MUNMAP), PERF_CONST(RECORD_AUX), PERF_CONST(RECORD_ITRACE_START), PERF_CONST(RECORD_LOST_SAMPLES), diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 349c68144e55..42edd68ce0b8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -357,6 +357,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->mmap = process_event_stub; if (tool->mmap2 == NULL) tool->mmap2 = process_event_stub; + if (tool->munmap == NULL) + tool->munmap = process_event_stub; if (tool->comm == NULL) tool->comm = process_event_stub; if (tool->fork == NULL) @@ -480,6 +482,20 @@ static void perf_event__mmap2_swap(union perf_event *event, swap_sample_id_all(event, data); } } + +static void perf_event__munmap_swap(union perf_event *event, bool sample_id_all) +{ + event->munmap.start = bswap_64(event->munmap.start); + event->munmap.len = bswap_64(event->munmap.len); + + if (sample_id_all) { + void *data = &event->munmap.len; + + data += sizeof(event->munmap.len); + swap_sample_id_all(event, data); + } +} + static void perf_event__task_swap(union perf_event *event, bool sample_id_all) { event->fork.pid = bswap_32(event->fork.pid); @@ -773,6 +789,7 @@ typedef void (*perf_event__swap_op)(union perf_event *event, static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_MMAP] = perf_event__mmap_swap, [PERF_RECORD_MMAP2] = perf_event__mmap2_swap, + [PERF_RECORD_MUNMAP] = perf_event__munmap_swap, [PERF_RECORD_COMM] = perf_event__comm_swap, [PERF_RECORD_FORK] = perf_event__task_swap, [PERF_RECORD_EXIT] = perf_event__task_swap, @@ -1237,6 +1254,8 @@ static int machines__deliver_event(struct machines *machines, if (event->header.misc & PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT) ++evlist->stats.nr_proc_map_timeout; return tool->mmap2(tool, event, sample, machine); + case PERF_RECORD_MUNMAP: + return tool->munmap(tool, event, sample, machine); case PERF_RECORD_COMM: return tool->comm(tool, event, sample, machine); case PERF_RECORD_FORK: diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index ac2590a3de2d..66291063b8b2 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -39,6 +39,7 @@ struct perf_tool { read; event_op mmap, mmap2, + munmap, comm, fork, exit,