perf intel-pt: Support decoding of guest kernel
authorAdrian Hunter <adrian.hunter@intel.com>
Thu, 18 Feb 2021 09:57:57 +0000 (11:57 +0200)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 18 Feb 2021 19:14:49 +0000 (16:14 -0300)
The guest kernel can be found from any guest thread belonging to the guest
machine. The guest machine is associated with the current host process pid.
An idle thread (pid=tid=0) is created as a vehicle from which to find the
guest kernel map.

Decoding guest user space is not supported.

Synthesized samples just need the cpumode set for the guest.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Acked-by: Andi Kleen <ak@linux.intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: https://lore.kernel.org/r/20210218095801.19576-8-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/intel-pt.c

index ddb8e6c..29d8717 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) {
@@ -1101,6 +1160,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);
@@ -1315,8 +1375,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 +1893,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;