From: Namhyung Kim <namhy...@kernel.org>

Add a new event type for deferred callchains and a new callback for the
struct perf_tool.  For now it doesn't actually handle the deferred
callchains but it just marks the sample if it has the PERF_CONTEXT_
USER_DEFFERED in the callchain array.

At least, perf report can dump the raw data with this change.  Actually
this requires the next commit to enable attr.defer_callchain, but if you
already have a data file, it'll show the following result.

  $ perf report -D
  ...
  0x5...@perf.data [0x40]: event: 22
  .
  . ... raw event: size 64 bytes
  .  0000:  16 00 00 00 02 00 40 00 02 00 00 00 00 00 00 00  ......@.........
  .  0010:  00 fe ff ff ff ff ff ff 4b d3 3f 25 45 7f 00 00  ........K.?%E...
  .  0020:  21 03 00 00 21 03 00 00 43 02 12 ab 05 00 00 00  !...!...C.......
  .  0030:  00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................

  0 24344920643 0x5fe0 [0x40]: PERF_RECORD_CALLCHAIN_DEFERRED(IP, 0x2): 
801/801: 0
  ... FP chain: nr:2
  .....  0: fffffffffffffe00
  .....  1: 00007f45253fd34b
  : unhandled!

Signed-off-by: Namhyung Kim <namhy...@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoim...@kernel.org>
---
 tools/lib/perf/include/perf/event.h       |  7 +++++++
 tools/perf/util/event.c                   |  1 +
 tools/perf/util/evsel.c                   | 15 +++++++++++++++
 tools/perf/util/machine.c                 |  1 +
 tools/perf/util/perf_event_attr_fprintf.c |  1 +
 tools/perf/util/sample.h                  |  3 ++-
 tools/perf/util/session.c                 | 17 +++++++++++++++++
 tools/perf/util/tool.c                    |  1 +
 tools/perf/util/tool.h                    |  3 ++-
 9 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/tools/lib/perf/include/perf/event.h 
b/tools/lib/perf/include/perf/event.h
index 37bb7771d914..f643a6a2b9fc 100644
--- a/tools/lib/perf/include/perf/event.h
+++ b/tools/lib/perf/include/perf/event.h
@@ -151,6 +151,12 @@ struct perf_record_switch {
        __u32                    next_prev_tid;
 };
 
+struct perf_record_callchain_deferred {
+       struct perf_event_header header;
+       __u64                    nr;
+       __u64                    ips[];
+};
+
 struct perf_record_header_attr {
        struct perf_event_header header;
        struct perf_event_attr   attr;
@@ -494,6 +500,7 @@ union perf_event {
        struct perf_record_read                 read;
        struct perf_record_throttle             throttle;
        struct perf_record_sample               sample;
+       struct perf_record_callchain_deferred   callchain_deferred;
        struct perf_record_bpf_event            bpf;
        struct perf_record_ksymbol              ksymbol;
        struct perf_record_text_poke_event      text_poke;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index aac96d5d1917..8cdec373db44 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -58,6 +58,7 @@ static const char *perf_event__names[] = {
        [PERF_RECORD_CGROUP]                    = "CGROUP",
        [PERF_RECORD_TEXT_POKE]                 = "TEXT_POKE",
        [PERF_RECORD_AUX_OUTPUT_HW_ID]          = "AUX_OUTPUT_HW_ID",
+       [PERF_RECORD_CALLCHAIN_DEFERRED]        = "CALLCHAIN_DEFERRED",
        [PERF_RECORD_HEADER_ATTR]               = "ATTR",
        [PERF_RECORD_HEADER_EVENT_TYPE]         = "EVENT_TYPE",
        [PERF_RECORD_HEADER_TRACING_DATA]       = "TRACING_DATA",
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index d22c5df1701e..09b9735f2fb1 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2817,6 +2817,18 @@ int evsel__parse_sample(struct evsel *evsel, union 
perf_event *event,
        data->data_src = PERF_MEM_DATA_SRC_NONE;
        data->vcpu = -1;
 
+       if (event->header.type == PERF_RECORD_CALLCHAIN_DEFERRED) {
+               const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
+
+               data->callchain = (struct ip_callchain 
*)&event->callchain_deferred.nr;
+               if (data->callchain->nr > max_callchain_nr)
+                       return -EFAULT;
+
+               if (evsel->core.attr.sample_id_all)
+                       perf_evsel__parse_id_sample(evsel, event, data);
+               return 0;
+       }
+
        if (event->header.type != PERF_RECORD_SAMPLE) {
                if (!evsel->core.attr.sample_id_all)
                        return 0;
@@ -2947,6 +2959,9 @@ int evsel__parse_sample(struct evsel *evsel, union 
perf_event *event,
                if (data->callchain->nr > max_callchain_nr)
                        return -EFAULT;
                sz = data->callchain->nr * sizeof(u64);
+               if (evsel->core.attr.defer_callchain && data->callchain->nr >= 
1 &&
+                   data->callchain->ips[data->callchain->nr - 1] == 
PERF_CONTEXT_USER_DEFERRED)
+                       data->deferred_callchain = true;
                OVERFLOW_CHECK(array, sz, max_size);
                array = (void *)array + sz;
        }
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 27d5345d2b30..9da467886bc6 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2087,6 +2087,7 @@ static int add_callchain_ip(struct thread *thread,
                                *cpumode = PERF_RECORD_MISC_KERNEL;
                                break;
                        case PERF_CONTEXT_USER:
+                       case PERF_CONTEXT_USER_DEFERRED:
                                *cpumode = PERF_RECORD_MISC_USER;
                                break;
                        default:
diff --git a/tools/perf/util/perf_event_attr_fprintf.c 
b/tools/perf/util/perf_event_attr_fprintf.c
index 59fbbba79697..113845b35110 100644
--- a/tools/perf/util/perf_event_attr_fprintf.c
+++ b/tools/perf/util/perf_event_attr_fprintf.c
@@ -321,6 +321,7 @@ int perf_event_attr__fprintf(FILE *fp, struct 
perf_event_attr *attr,
        PRINT_ATTRf(inherit_thread, p_unsigned);
        PRINT_ATTRf(remove_on_exec, p_unsigned);
        PRINT_ATTRf(sigtrap, p_unsigned);
+       PRINT_ATTRf(defer_callchain, p_unsigned);
 
        PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, 
p_unsigned, false);
        PRINT_ATTRf(bp_type, p_unsigned);
diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
index 70b2c3135555..010659dc80f8 100644
--- a/tools/perf/util/sample.h
+++ b/tools/perf/util/sample.h
@@ -108,7 +108,8 @@ struct perf_sample {
                u16 p_stage_cyc;
                u16 retire_lat;
        };
-       bool no_hw_idx;         /* No hw_idx collected in branch_stack */
+       bool no_hw_idx;                 /* No hw_idx collected in branch_stack 
*/
+       bool deferred_callchain;        /* Has deferred user callchains */
        char insn[MAX_INSN];
        void *raw_data;
        struct ip_callchain *callchain;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 507e6cba9545..493070180279 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -714,6 +714,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
        [PERF_RECORD_CGROUP]              = perf_event__cgroup_swap,
        [PERF_RECORD_TEXT_POKE]           = perf_event__text_poke_swap,
        [PERF_RECORD_AUX_OUTPUT_HW_ID]    = perf_event__all64_swap,
+       [PERF_RECORD_CALLCHAIN_DEFERRED]  = perf_event__all64_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,
@@ -1107,6 +1108,19 @@ static void dump_sample(struct evsel *evsel, union 
perf_event *event,
                sample_read__printf(sample, evsel->core.attr.read_format);
 }
 
+static void dump_deferred_callchain(struct evsel *evsel, union perf_event 
*event,
+                                   struct perf_sample *sample)
+{
+       if (!dump_trace)
+               return;
+
+       printf("(IP, 0x%x): %d/%d: %#" PRIx64 "\n",
+              event->header.misc, sample->pid, sample->tid, sample->ip);
+
+       if (evsel__has_callchain(evsel))
+               callchain__printf(evsel, sample);
+}
+
 static void dump_read(struct evsel *evsel, union perf_event *event)
 {
        struct perf_record_read *read_event = &event->read;
@@ -1337,6 +1351,9 @@ static int machines__deliver_event(struct machines 
*machines,
                return tool->text_poke(tool, event, sample, machine);
        case PERF_RECORD_AUX_OUTPUT_HW_ID:
                return tool->aux_output_hw_id(tool, event, sample, machine);
+       case PERF_RECORD_CALLCHAIN_DEFERRED:
+               dump_deferred_callchain(evsel, event, sample);
+               return tool->callchain_deferred(tool, event, sample, evsel, 
machine);
        default:
                ++evlist->stats.nr_unknown_events;
                return -1;
diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c
index 3b7f390f26eb..e78f16de912e 100644
--- a/tools/perf/util/tool.c
+++ b/tools/perf/util/tool.c
@@ -259,6 +259,7 @@ void perf_tool__init(struct perf_tool *tool, bool 
ordered_events)
        tool->read = process_event_sample_stub;
        tool->throttle = process_event_stub;
        tool->unthrottle = process_event_stub;
+       tool->callchain_deferred = process_event_sample_stub;
        tool->attr = process_event_synth_attr_stub;
        tool->event_update = process_event_synth_event_update_stub;
        tool->tracing_data = process_event_synth_tracing_data_stub;
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index db1c7642b0d1..9987bbde6d5e 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -42,7 +42,8 @@ enum show_feature_header {
 
 struct perf_tool {
        event_sample    sample,
-                       read;
+                       read,
+                       callchain_deferred;
        event_op        mmap,
                        mmap2,
                        comm,
-- 
2.48.1


Reply via email to