intel_bts synthesizes samples.  Fill in the new flags and
insn_len members with instruction information.

Signed-off-by: Adrian Hunter <adrian.hun...@intel.com>
---
 tools/perf/util/intel-bts.c | 128 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 124 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c
index dbd8162..a2ac54a 100644
--- a/tools/perf/util/intel-bts.c
+++ b/tools/perf/util/intel-bts.c
@@ -26,11 +26,13 @@
 #include "evlist.h"
 #include "machine.h"
 #include "session.h"
+#include "color.h"
 #include "util.h"
 #include "pmu.h"
 #include "debug.h"
 #include "tsc.h"
 #include "auxtrace.h"
+#include "intel-pt-decoder/intel-pt-insn-decoder.h"
 #include "intel-bts.h"
 
 #define MAX_TIMESTAMP (~0ULL)
@@ -81,6 +83,7 @@ struct intel_bts {
        bool                            cap_user_time_zero;
        struct itrace_synth_opts        synth_opts;
        bool                            sample_branches;
+       u32                             branches_filter;
        u64                             branches_sample_type;
        u64                             branches_id;
        size_t                          branches_event_size;
@@ -97,6 +100,8 @@ struct intel_bts_queue {
        pid_t                   tid;
        int                     cpu;
        u64                     time;
+       struct intel_pt_insn    intel_pt_insn;
+       u32                     sample_flags;
 };
 
 struct branch {
@@ -304,6 +309,8 @@ static int intel_bts_synth_branch_sample(struct 
intel_bts_queue *btsq,
        sample.stream_id = btsq->bts->branches_id;
        sample.period = 1;
        sample.cpu = btsq->cpu;
+       sample.flags = btsq->sample_flags;
+       sample.insn_len = btsq->intel_pt_insn.length;
 
        if (bts->synth_opts.inject) {
                event.sample.header.size = bts->branches_event_size;
@@ -323,11 +330,116 @@ static int intel_bts_synth_branch_sample(struct 
intel_bts_queue *btsq,
        return ret;
 }
 
+static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip)
+{
+       struct machine *machine = btsq->bts->machine;
+       struct thread *thread;
+       struct addr_location al;
+       unsigned char buf[1024];
+       size_t bufsz;
+       ssize_t len;
+       int x86_64;
+       uint8_t cpumode;
+
+       bufsz = intel_pt_insn_max_size();
+
+       if (machine__kernel_ip(machine, ip))
+               cpumode = PERF_RECORD_MISC_KERNEL;
+       else
+               cpumode = PERF_RECORD_MISC_USER;
+
+       thread = machine__find_thread(machine, -1, btsq->tid);
+       if (!thread)
+               return -1;
+
+       thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al);
+       if (!al.map || !al.map->dso)
+               return -1;
+
+       len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, bufsz);
+       if (len <= 0)
+               return -1;
+
+       /* Load maps to ensure dso->is_64_bit has been updated */
+       map__load(al.map, machine->symbol_filter);
+
+       x86_64 = al.map->dso->is_64_bit;
+
+       if (intel_pt_get_insn(buf, len, x86_64, &btsq->intel_pt_insn))
+               return -1;
+
+       return 0;
+}
+
+static int intel_bts_synth_error(struct intel_bts *bts, int cpu, pid_t pid,
+                                pid_t tid, u64 ip)
+{
+       union perf_event event;
+       const char *msg = "Failed to get instruction";
+       int code = EILSEQ;
+       int err;
+
+       auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE 
, code,
+                          cpu, pid, tid, ip, msg);
+
+       err = perf_session__deliver_synth_event(bts->session, &event, NULL);
+       if (err)
+               pr_err("Intel BTS: failed to deliver error event, error %d\n",
+                      err);
+
+       return err;
+}
+
+static int intel_bts_get_branch_type(struct intel_bts_queue *btsq,
+                                    struct branch *branch)
+{
+       int err;
+
+       if (!branch->from) {
+               if (branch->to)
+                       btsq->sample_flags = PERF_IP_FLAG_BRANCH |
+                                            PERF_IP_FLAG_TRACE_BEGIN;
+               else
+                       btsq->sample_flags = 0;
+               btsq->intel_pt_insn.length = 0;
+       } else if (!branch->to) {
+               btsq->sample_flags = PERF_IP_FLAG_BRANCH |
+                                    PERF_IP_FLAG_TRACE_END;
+               btsq->intel_pt_insn.length = 0;
+       } else {
+               err = intel_bts_get_next_insn(btsq, branch->from);
+               if (err) {
+                       btsq->sample_flags = 0;
+                       btsq->intel_pt_insn.length = 0;
+                       if (!btsq->bts->synth_opts.errors)
+                               return 0;
+                       err = intel_bts_synth_error(btsq->bts, btsq->cpu,
+                                                   btsq->pid, btsq->tid,
+                                                   branch->from);
+                       return err;
+               }
+               btsq->sample_flags = intel_pt_insn_type(btsq->intel_pt_insn.op);
+               /* Check for an async branch into the kernel */
+               if (!machine__kernel_ip(btsq->bts->machine, branch->from) &&
+                   machine__kernel_ip(btsq->bts->machine, branch->to) &&
+                   btsq->sample_flags != (PERF_IP_FLAG_BRANCH |
+                                          PERF_IP_FLAG_CALL |
+                                          PERF_IP_FLAG_SYSCALLRET))
+                       btsq->sample_flags = PERF_IP_FLAG_BRANCH |
+                                            PERF_IP_FLAG_CALL |
+                                            PERF_IP_FLAG_ASYNC |
+                                            PERF_IP_FLAG_INTERRUPT;
+       }
+
+       return 0;
+}
+
 static int intel_bts_process_buffer(struct intel_bts_queue *btsq,
                                    struct auxtrace_buffer *buffer)
 {
        struct branch *branch;
-       size_t sz;
+       size_t sz, bsz = sizeof(struct branch);
+       u32 filter = btsq->bts->branches_filter;
        int err = 0;
 
        if (buffer->use_data) {
@@ -341,14 +453,15 @@ static int intel_bts_process_buffer(struct 
intel_bts_queue *btsq,
        if (!btsq->bts->sample_branches)
                return 0;
 
-       while (sz > sizeof(struct branch)) {
+       for (; sz > bsz; branch += 1, sz -= bsz) {
                if (!branch->from && !branch->to)
                        continue;
+               intel_bts_get_branch_type(btsq, branch);
+               if (filter && !(filter & btsq->sample_flags))
+                       continue;
                err = intel_bts_synth_branch_sample(btsq, branch);
                if (err)
                        break;
-               branch += 1;
-               sz -= sizeof(struct branch);
        }
        return err;
 }
@@ -804,6 +917,13 @@ int intel_bts_process_auxtrace_info(union perf_event 
*event,
        else
                itrace_synth_opts__set_default(&bts->synth_opts);
 
+       if (bts->synth_opts.calls)
+               bts->branches_filter |= PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
+                                       PERF_IP_FLAG_TRACE_END;
+       if (bts->synth_opts.returns)
+               bts->branches_filter |= PERF_IP_FLAG_RETURN |
+                                       PERF_IP_FLAG_TRACE_BEGIN;
+
        err = intel_bts_synth_events(bts, session);
        if (err)
                goto err_free_queues;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to