Merge tag 'arm-soc/for-5.12/drivers-part2' of https://github.com/Broadcom/stblinux...
[linux-2.6-microblaze.git] / tools / perf / util / intel-pt.c
index ddb8e6c..f6e28ac 100644 (file)
@@ -163,6 +163,9 @@ struct intel_pt_queue {
        int switch_state;
        pid_t next_tid;
        struct thread *thread;
+       struct machine *guest_machine;
+       struct thread *unknown_guest_thread;
+       pid_t guest_machine_pid;
        bool exclude_kernel;
        bool have_sample;
        u64 time;
@@ -550,13 +553,59 @@ static void intel_pt_cache_invalidate(struct dso *dso, struct machine *machine,
        auxtrace_cache__remove(dso->auxtrace_cache, offset);
 }
 
-static inline u8 intel_pt_cpumode(struct intel_pt *pt, uint64_t ip)
+static inline bool intel_pt_guest_kernel_ip(uint64_t ip)
 {
-       return ip >= pt->kernel_start ?
+       /* Assumes 64-bit kernel */
+       return ip & (1ULL << 63);
+}
+
+static inline u8 intel_pt_nr_cpumode(struct intel_pt_queue *ptq, uint64_t ip, bool nr)
+{
+       if (nr) {
+               return intel_pt_guest_kernel_ip(ip) ?
+                      PERF_RECORD_MISC_GUEST_KERNEL :
+                      PERF_RECORD_MISC_GUEST_USER;
+       }
+
+       return ip >= ptq->pt->kernel_start ?
               PERF_RECORD_MISC_KERNEL :
               PERF_RECORD_MISC_USER;
 }
 
+static inline u8 intel_pt_cpumode(struct intel_pt_queue *ptq, uint64_t from_ip, uint64_t to_ip)
+{
+       /* No support for non-zero CS base */
+       if (from_ip)
+               return intel_pt_nr_cpumode(ptq, from_ip, ptq->state->from_nr);
+       return intel_pt_nr_cpumode(ptq, to_ip, ptq->state->to_nr);
+}
+
+static int intel_pt_get_guest(struct intel_pt_queue *ptq)
+{
+       struct machines *machines = &ptq->pt->session->machines;
+       struct machine *machine;
+       pid_t pid = ptq->pid <= 0 ? DEFAULT_GUEST_KERNEL_ID : ptq->pid;
+
+       if (ptq->guest_machine && pid == ptq->guest_machine_pid)
+               return 0;
+
+       ptq->guest_machine = NULL;
+       thread__zput(ptq->unknown_guest_thread);
+
+       machine = machines__find_guest(machines, pid);
+       if (!machine)
+               return -1;
+
+       ptq->unknown_guest_thread = machine__idle_thread(machine);
+       if (!ptq->unknown_guest_thread)
+               return -1;
+
+       ptq->guest_machine = machine;
+       ptq->guest_machine_pid = pid;
+
+       return 0;
+}
+
 static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
                                   uint64_t *insn_cnt_ptr, uint64_t *ip,
                                   uint64_t to_ip, uint64_t max_insn_cnt,
@@ -573,19 +622,29 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
        u64 offset, start_offset, start_ip;
        u64 insn_cnt = 0;
        bool one_map = true;
+       bool nr;
 
        intel_pt_insn->length = 0;
 
        if (to_ip && *ip == to_ip)
                goto out_no_cache;
 
-       cpumode = intel_pt_cpumode(ptq->pt, *ip);
+       nr = ptq->state->to_nr;
+       cpumode = intel_pt_nr_cpumode(ptq, *ip, nr);
 
-       thread = ptq->thread;
-       if (!thread) {
-               if (cpumode != PERF_RECORD_MISC_KERNEL)
+       if (nr) {
+               if (cpumode != PERF_RECORD_MISC_GUEST_KERNEL ||
+                   intel_pt_get_guest(ptq))
                        return -EINVAL;
-               thread = ptq->pt->unknown_thread;
+               machine = ptq->guest_machine;
+               thread = ptq->unknown_guest_thread;
+       } else {
+               thread = ptq->thread;
+               if (!thread) {
+                       if (cpumode != PERF_RECORD_MISC_KERNEL)
+                               return -EINVAL;
+                       thread = ptq->pt->unknown_thread;
+               }
        }
 
        while (1) {
@@ -733,8 +792,14 @@ static int __intel_pt_pgd_ip(uint64_t ip, void *data)
        u8 cpumode;
        u64 offset;
 
-       if (ip >= ptq->pt->kernel_start)
+       if (ptq->state->to_nr) {
+               if (intel_pt_guest_kernel_ip(ip))
+                       return intel_pt_match_pgd_ip(ptq->pt, ip, ip, NULL);
+               /* No support for decoding guest user space */
+               return -EINVAL;
+       } else if (ip >= ptq->pt->kernel_start) {
                return intel_pt_match_pgd_ip(ptq->pt, ip, ip, NULL);
+       }
 
        cpumode = PERF_RECORD_MISC_USER;
 
@@ -1101,6 +1166,7 @@ static void intel_pt_free_queue(void *priv)
        if (!ptq)
                return;
        thread__zput(ptq->thread);
+       thread__zput(ptq->unknown_guest_thread);
        intel_pt_decoder_free(ptq->decoder);
        zfree(&ptq->event_buf);
        zfree(&ptq->last_branch);
@@ -1135,13 +1201,16 @@ static void intel_pt_sample_flags(struct intel_pt_queue *ptq)
        if (ptq->state->flags & INTEL_PT_ABORT_TX) {
                ptq->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT;
        } else if (ptq->state->flags & INTEL_PT_ASYNC) {
-               if (ptq->state->to_ip)
+               if (!ptq->state->to_ip)
+                       ptq->flags = PERF_IP_FLAG_BRANCH |
+                                    PERF_IP_FLAG_TRACE_END;
+               else if (ptq->state->from_nr && !ptq->state->to_nr)
+                       ptq->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
+                                    PERF_IP_FLAG_VMEXIT;
+               else
                        ptq->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
                                     PERF_IP_FLAG_ASYNC |
                                     PERF_IP_FLAG_INTERRUPT;
-               else
-                       ptq->flags = PERF_IP_FLAG_BRANCH |
-                                    PERF_IP_FLAG_TRACE_END;
                ptq->insn_len = 0;
        } else {
                if (ptq->state->from_ip)
@@ -1315,8 +1384,8 @@ static void intel_pt_prep_b_sample(struct intel_pt *pt,
                sample->time = tsc_to_perf_time(ptq->timestamp, &pt->tc);
 
        sample->ip = ptq->state->from_ip;
-       sample->cpumode = intel_pt_cpumode(pt, sample->ip);
        sample->addr = ptq->state->to_ip;
+       sample->cpumode = intel_pt_cpumode(ptq, sample->ip, sample->addr);
        sample->period = 1;
        sample->flags = ptq->flags;
 
@@ -1833,10 +1902,7 @@ static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
        else
                sample.ip = ptq->state->from_ip;
 
-       /* No support for guest mode at this time */
-       cpumode = sample.ip < ptq->pt->kernel_start ?
-                 PERF_RECORD_MISC_USER :
-                 PERF_RECORD_MISC_KERNEL;
+       cpumode = intel_pt_cpumode(ptq, sample.ip, 0);
 
        event->sample.header.misc = cpumode | PERF_RECORD_MISC_EXACT_IP;
 
@@ -2105,7 +2171,27 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
        }
 
        if (pt->sample_branches) {
-               err = intel_pt_synth_branch_sample(ptq);
+               if (state->from_nr != state->to_nr &&
+                   state->from_ip && state->to_ip) {
+                       struct intel_pt_state *st = (struct intel_pt_state *)state;
+                       u64 to_ip = st->to_ip;
+                       u64 from_ip = st->from_ip;
+
+                       /*
+                        * perf cannot handle having different machines for ip
+                        * and addr, so create 2 branches.
+                        */
+                       st->to_ip = 0;
+                       err = intel_pt_synth_branch_sample(ptq);
+                       if (err)
+                               return err;
+                       st->from_ip = 0;
+                       st->to_ip = to_ip;
+                       err = intel_pt_synth_branch_sample(ptq);
+                       st->from_ip = from_ip;
+               } else {
+                       err = intel_pt_synth_branch_sample(ptq);
+               }
                if (err)
                        return err;
        }