Merge tag 'perf-core-for-mingo-20160929' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / tools / perf / util / intel-pt.c
index 551ff6f..dc041d4 100644 (file)
@@ -103,6 +103,9 @@ struct intel_pt {
        unsigned max_non_turbo_ratio;
 
        unsigned long num_events;
+
+       char *filter;
+       struct addr_filters filts;
 };
 
 enum switch_state {
@@ -241,7 +244,7 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
        }
 
        queue = &ptq->pt->queues.queue_array[ptq->queue_nr];
-
+next:
        buffer = auxtrace_buffer__next(queue, buffer);
        if (!buffer) {
                if (old_buffer)
@@ -264,9 +267,6 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
            intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer))
                return -ENOMEM;
 
-       if (old_buffer)
-               auxtrace_buffer__drop_data(old_buffer);
-
        if (buffer->use_data) {
                b->len = buffer->use_size;
                b->buf = buffer->use_data;
@@ -276,6 +276,16 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
        }
        b->ref_timestamp = buffer->reference;
 
+       /*
+        * If in snapshot mode and the buffer has no usable data, get next
+        * buffer and again check overlap against old_buffer.
+        */
+       if (ptq->pt->snapshot_mode && !b->len)
+               goto next;
+
+       if (old_buffer)
+               auxtrace_buffer__drop_data(old_buffer);
+
        if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode &&
                                                      !buffer->consecutive)) {
                b->consecutive = false;
@@ -477,7 +487,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
                start_ip = *ip;
 
                /* Load maps to ensure dso->is_64_bit has been updated */
-               map__load(al.map, machine->symbol_filter);
+               map__load(al.map);
 
                x86_64 = al.map->dso->is_64_bit;
 
@@ -541,6 +551,76 @@ out_no_cache:
        return 0;
 }
 
+static bool intel_pt_match_pgd_ip(struct intel_pt *pt, uint64_t ip,
+                                 uint64_t offset, const char *filename)
+{
+       struct addr_filter *filt;
+       bool have_filter   = false;
+       bool hit_tracestop = false;
+       bool hit_filter    = false;
+
+       list_for_each_entry(filt, &pt->filts.head, list) {
+               if (filt->start)
+                       have_filter = true;
+
+               if ((filename && !filt->filename) ||
+                   (!filename && filt->filename) ||
+                   (filename && strcmp(filename, filt->filename)))
+                       continue;
+
+               if (!(offset >= filt->addr && offset < filt->addr + filt->size))
+                       continue;
+
+               intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s hit filter: %s offset %#"PRIx64" size %#"PRIx64"\n",
+                            ip, offset, filename ? filename : "[kernel]",
+                            filt->start ? "filter" : "stop",
+                            filt->addr, filt->size);
+
+               if (filt->start)
+                       hit_filter = true;
+               else
+                       hit_tracestop = true;
+       }
+
+       if (!hit_tracestop && !hit_filter)
+               intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s is not in a filter region\n",
+                            ip, offset, filename ? filename : "[kernel]");
+
+       return hit_tracestop || (have_filter && !hit_filter);
+}
+
+static int __intel_pt_pgd_ip(uint64_t ip, void *data)
+{
+       struct intel_pt_queue *ptq = data;
+       struct thread *thread;
+       struct addr_location al;
+       u8 cpumode;
+       u64 offset;
+
+       if (ip >= ptq->pt->kernel_start)
+               return intel_pt_match_pgd_ip(ptq->pt, ip, ip, NULL);
+
+       cpumode = PERF_RECORD_MISC_USER;
+
+       thread = ptq->thread;
+       if (!thread)
+               return -EINVAL;
+
+       thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al);
+       if (!al.map || !al.map->dso)
+               return -EINVAL;
+
+       offset = al.map->map_ip(al.map, ip);
+
+       return intel_pt_match_pgd_ip(ptq->pt, ip, offset,
+                                    al.map->dso->long_name);
+}
+
+static bool intel_pt_pgd_ip(uint64_t ip, void *data)
+{
+       return __intel_pt_pgd_ip(ip, data) > 0;
+}
+
 static bool intel_pt_get_config(struct intel_pt *pt,
                                struct perf_event_attr *attr, u64 *config)
 {
@@ -717,6 +797,9 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
        params.tsc_ctc_ratio_n = pt->tsc_ctc_ratio_n;
        params.tsc_ctc_ratio_d = pt->tsc_ctc_ratio_d;
 
+       if (pt->filts.cnt > 0)
+               params.pgd_ip = intel_pt_pgd_ip;
+
        if (pt->synth_opts.instructions) {
                if (pt->synth_opts.period) {
                        switch (pt->synth_opts.period_type) {
@@ -1294,7 +1377,7 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
        if (!map)
                return 0;
 
-       if (map__load(map, machine->symbol_filter))
+       if (map__load(map))
                return 0;
 
        start = dso__first_symbol(map->dso, MAP__FUNCTION);
@@ -1767,6 +1850,8 @@ static void intel_pt_free(struct perf_session *session)
        intel_pt_free_events(session);
        session->auxtrace = NULL;
        thread__put(pt->unknown_thread);
+       addr_filters__exit(&pt->filts);
+       zfree(&pt->filter);
        free(pt);
 }
 
@@ -2016,6 +2101,8 @@ static const char * const intel_pt_info_fmts[] = {
        [INTEL_PT_TSC_CTC_N]            = "  TSC:CTC numerator   %"PRIu64"\n",
        [INTEL_PT_TSC_CTC_D]            = "  TSC:CTC denominator %"PRIu64"\n",
        [INTEL_PT_CYC_BIT]              = "  CYC bit             %#"PRIx64"\n",
+       [INTEL_PT_MAX_NONTURBO_RATIO]   = "  Max non-turbo ratio %"PRIu64"\n",
+       [INTEL_PT_FILTER_STR_LEN]       = "  Filter string len.  %"PRIu64"\n",
 };
 
 static void intel_pt_print_info(u64 *arr, int start, int finish)
@@ -2029,12 +2116,28 @@ static void intel_pt_print_info(u64 *arr, int start, int finish)
                fprintf(stdout, intel_pt_info_fmts[i], arr[i]);
 }
 
+static void intel_pt_print_info_str(const char *name, const char *str)
+{
+       if (!dump_trace)
+               return;
+
+       fprintf(stdout, "  %-20s%s\n", name, str ? str : "");
+}
+
+static bool intel_pt_has(struct auxtrace_info_event *auxtrace_info, int pos)
+{
+       return auxtrace_info->header.size >=
+               sizeof(struct auxtrace_info_event) + (sizeof(u64) * (pos + 1));
+}
+
 int intel_pt_process_auxtrace_info(union perf_event *event,
                                   struct perf_session *session)
 {
        struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
        size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS;
        struct intel_pt *pt;
+       void *info_end;
+       u64 *info;
        int err;
 
        if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) +
@@ -2045,6 +2148,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
        if (!pt)
                return -ENOMEM;
 
+       addr_filters__init(&pt->filts);
+
        perf_config(intel_pt_perf_config, pt);
 
        err = auxtrace_queues__init(&pt->queues);
@@ -2069,8 +2174,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
        intel_pt_print_info(&auxtrace_info->priv[0], INTEL_PT_PMU_TYPE,
                            INTEL_PT_PER_CPU_MMAPS);
 
-       if (auxtrace_info->header.size >= sizeof(struct auxtrace_info_event) +
-                                       (sizeof(u64) * INTEL_PT_CYC_BIT)) {
+       if (intel_pt_has(auxtrace_info, INTEL_PT_CYC_BIT)) {
                pt->mtc_bit = auxtrace_info->priv[INTEL_PT_MTC_BIT];
                pt->mtc_freq_bits = auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS];
                pt->tsc_ctc_ratio_n = auxtrace_info->priv[INTEL_PT_TSC_CTC_N];
@@ -2080,6 +2184,54 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
                                    INTEL_PT_CYC_BIT);
        }
 
+       if (intel_pt_has(auxtrace_info, INTEL_PT_MAX_NONTURBO_RATIO)) {
+               pt->max_non_turbo_ratio =
+                       auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO];
+               intel_pt_print_info(&auxtrace_info->priv[0],
+                                   INTEL_PT_MAX_NONTURBO_RATIO,
+                                   INTEL_PT_MAX_NONTURBO_RATIO);
+       }
+
+       info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1;
+       info_end = (void *)info + auxtrace_info->header.size;
+
+       if (intel_pt_has(auxtrace_info, INTEL_PT_FILTER_STR_LEN)) {
+               size_t len;
+
+               len = auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN];
+               intel_pt_print_info(&auxtrace_info->priv[0],
+                                   INTEL_PT_FILTER_STR_LEN,
+                                   INTEL_PT_FILTER_STR_LEN);
+               if (len) {
+                       const char *filter = (const char *)info;
+
+                       len = roundup(len + 1, 8);
+                       info += len >> 3;
+                       if ((void *)info > info_end) {
+                               pr_err("%s: bad filter string length\n", __func__);
+                               err = -EINVAL;
+                               goto err_free_queues;
+                       }
+                       pt->filter = memdup(filter, len);
+                       if (!pt->filter) {
+                               err = -ENOMEM;
+                               goto err_free_queues;
+                       }
+                       if (session->header.needs_swap)
+                               mem_bswap_64(pt->filter, len);
+                       if (pt->filter[len - 1]) {
+                               pr_err("%s: filter string not null terminated\n", __func__);
+                               err = -EINVAL;
+                               goto err_free_queues;
+                       }
+                       err = addr_filters__parse_bare_filter(&pt->filts,
+                                                             filter);
+                       if (err)
+                               goto err_free_queues;
+               }
+               intel_pt_print_info_str("Filter string", pt->filter);
+       }
+
        pt->timeless_decoding = intel_pt_timeless_decoding(pt);
        pt->have_tsc = intel_pt_have_tsc(pt);
        pt->sampling_mode = false;
@@ -2121,11 +2273,13 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
                pt->switch_evsel = intel_pt_find_sched_switch(session->evlist);
                if (!pt->switch_evsel) {
                        pr_err("%s: missing sched_switch event\n", __func__);
+                       err = -EINVAL;
                        goto err_delete_thread;
                }
        } else if (pt->have_sched_switch == 2 &&
                   !intel_pt_find_switch(session->evlist)) {
                pr_err("%s: missing context_switch attribute flag\n", __func__);
+               err = -EINVAL;
                goto err_delete_thread;
        }
 
@@ -2149,7 +2303,9 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
        if (pt->tc.time_mult) {
                u64 tsc_freq = intel_pt_ns_to_ticks(pt, 1000000000);
 
-               pt->max_non_turbo_ratio = (tsc_freq + 50000000) / 100000000;
+               if (!pt->max_non_turbo_ratio)
+                       pt->max_non_turbo_ratio =
+                                       (tsc_freq + 50000000) / 100000000;
                intel_pt_log("TSC frequency %"PRIu64"\n", tsc_freq);
                intel_pt_log("Maximum non-turbo ratio %u\n",
                             pt->max_non_turbo_ratio);
@@ -2193,6 +2349,8 @@ err_free_queues:
        auxtrace_queues__free(&pt->queues);
        session->auxtrace = NULL;
 err_free:
+       addr_filters__exit(&pt->filts);
+       zfree(&pt->filter);
        free(pt);
        return err;
 }