[PATCH 15/21] perf report: Add delayed user data event processing

Jiri Olsa
2018-01-24T11:52:25Z

Adding support to process user data events and attach
their data into samples.

The logic is to copy and store every sample that has
delayed user data (PERF_RECORD_MISC_USER_DATA bit)
under per-thread list.

Then when there's the USER DATA event under this thread,
we go through that list and attach user data to matching
samples (using USER_DATA_ID value).

The processing data at this point are already sorted,
so we don't need to worry about wrongly skipping samples
by unordered USER DATA event. Also we connect only
data that matches same event.

However event loss is still possible, that's why we
match USER_DATA_ID on sample and USER DATA event.
We can remove not matching samples.

Link: http://lkml.kernel.org/n/tip-hacxbv4wzvpt50nf8wj6lk2w@git.kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
tools/perf/builtin-report.c | 127 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/thread.c | 1 +
tools/perf/util/thread.h | 8 +++
3 files changed, 136 insertions(+)

diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index a08e2c88070a..a1cd5fd793fc 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -293,6 +293,31 @@ perf_sample__process(struct perf_sample *sample, struct addr_location *al,
return hist_entry_iter__add(&iter, al, rep->max_stack, rep);
}

+static int
+perf_thread__add_user_data(struct thread *thread,
+ struct perf_sample *sample,
+ struct addr_location *al,
+ struct perf_evsel *evsel)
+{
+ struct user_data *entry;
+
+ entry = zalloc(sizeof(*entry));
+ if (!entry)
+ return -ENOMEM;
+
+ entry->al = *al;
+ entry->evsel = evsel;
+ INIT_LIST_HEAD(&entry->list);
+
+ if (perf_sample__copy(&entry->sample, sample)) {
+ free(entry);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&entry->list, &thread->user_data_list);
+ return 0;
+}
+
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -332,6 +357,9 @@ static int process_sample_event(struct perf_tool *tool,
if (al.map != NULL)
al.map->dso->hit = 1;

+ if (event->header.misc & PERF_RECORD_MISC_USER_DATA)
+ return perf_thread__add_user_data(al.thread, sample, &al, evsel);
+
ret = perf_sample__process(sample, &al, evsel, rep);
if (ret < 0)
pr_debug("problem adding hist entry, skipping event\n");
@@ -340,6 +368,104 @@ static int process_sample_event(struct perf_tool *tool,
return ret;
}

+static int
+perf_sample__add_user_callchain(struct perf_sample *sample,
+ struct perf_sample *user)
+{
+ struct ip_callchain *sc = sample->callchain;
+ struct ip_callchain *uc = user->callchain;
+ struct ip_callchain *new;
+ u64 nr = 1 + sc->nr + uc->nr;
+
+ new = zalloc(nr * sizeof(u64));
+ if (!new)
+ return -ENOMEM;
+
+ new->nr = nr;
+ memcpy(new->ips, sc->ips, sc->nr * sizeof(u64));
+ memcpy(new->ips + sc->nr, uc->ips, uc->nr * sizeof(u64));
+
+ free(sample->callchain);
+ sample->callchain = new;
+ return 0;
+}
+
+static int
+perf_sample__add_user_data(struct perf_sample *sample,
+ struct perf_sample *user,
+ u64 type)
+{
+ int ret = 0;
+
+ if (type & PERF_SAMPLE_CALLCHAIN)
+ ret = perf_sample__add_user_callchain(sample, user);
+
+ return ret;
+}
+
+static int
+user_data__process(struct user_data *entry, struct perf_sample *sample,
+ struct user_data_event *event, struct report *rep)
+{
+ int ret;
+
+ ret = perf_sample__add_user_data(&entry->sample, sample, event->type);
+ if (ret)
+ return ret;
+
+ return perf_sample__process(&entry->sample, &entry->al, entry->evsel, rep);
+}
+
+static int
+thread__flush_user_data(struct thread *thread,
+ struct user_data_event *event,
+ struct perf_sample *sample,
+ struct report *rep)
+{
+ struct user_data *entry, *p;
+ int ret = 0;
+
+ list_for_each_entry_safe(entry, p, &thread->user_data_list, list) {
+ /* different event, skip it */
+ if (entry->sample.id != sample->id)
+ continue;
+
+ /*
+ * We process only matching IDs, if we don't match in here
+ * it means we've lot master sample, remove user data event
+ * without any action.
+ */
+ if (entry->sample.user_data_id == sample->user_data_id) {
+ ret = user_data__process(entry, sample, event, rep);
+ if (ret)
+ pr_debug("problem adding hist entry, skipping event\n");
+ }
+
+ list_del(&entry->list);
+ perf_sample__free(&entry->sample);
+ free(entry);
+ }
+
+ return ret;
+}
+
+static int
+process_user_data_event(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel __maybe_unused,
+ struct machine *machine)
+{
+ struct report *rep = container_of(tool, struct report, tool);
+ struct thread *thread = machine__findnew_thread(machine, sample->pid,
+ sample->tid);
+
+ if (thread == NULL)
+ return -1;
+
+ return thread__flush_user_data(thread, &event->user_data, sample, rep);
+}
+
static int process_read_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
@@ -1025,6 +1151,7 @@ int cmd_report(int argc, const char **argv)
struct report report = {
.tool = {
.sample = process_sample_event,
+ .user_data = process_user_data_event,
.mmap = perf_event__process_mmap,
.mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 68b65b10579b..e9008245a421 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -48,6 +48,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
INIT_LIST_HEAD(&thread->comm_list);
init_rwsem(&thread->namespaces_lock);
init_rwsem(&thread->comm_lock);
+ INIT_LIST_HEAD(&thread->user_data_list);

comm_str = malloc(32);
if (!comm_str)
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 40cfa36c022a..e78b295bbcdb 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -15,6 +15,13 @@
struct thread_stack;
struct unwind_libunwind_ops;

+struct user_data {
+ struct perf_sample sample;
+ struct addr_location al;
+ struct list_head list;
+ struct perf_evsel *evsel;
+};
+
struct thread {
union {
struct rb_node rb_node;
@@ -34,6 +41,7 @@ struct thread {
struct rw_semaphore namespaces_lock;
struct list_head comm_list;
struct rw_semaphore comm_lock;
+ struct list_head user_data_list;
u64 db_id;

void *priv;
--
2.13.6