diff options
Diffstat (limited to 'tools/perf/util/intel-pt.c')
-rw-r--r-- | tools/perf/util/intel-pt.c | 176 |
1 files changed, 167 insertions, 9 deletions
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 551ff6f640be..dc041d4368c8 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -103,6 +103,9 @@ struct intel_pt { | |||
103 | unsigned max_non_turbo_ratio; | 103 | unsigned max_non_turbo_ratio; |
104 | 104 | ||
105 | unsigned long num_events; | 105 | unsigned long num_events; |
106 | |||
107 | char *filter; | ||
108 | struct addr_filters filts; | ||
106 | }; | 109 | }; |
107 | 110 | ||
108 | enum switch_state { | 111 | enum switch_state { |
@@ -241,7 +244,7 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) | |||
241 | } | 244 | } |
242 | 245 | ||
243 | queue = &ptq->pt->queues.queue_array[ptq->queue_nr]; | 246 | queue = &ptq->pt->queues.queue_array[ptq->queue_nr]; |
244 | 247 | next: | |
245 | buffer = auxtrace_buffer__next(queue, buffer); | 248 | buffer = auxtrace_buffer__next(queue, buffer); |
246 | if (!buffer) { | 249 | if (!buffer) { |
247 | if (old_buffer) | 250 | if (old_buffer) |
@@ -264,9 +267,6 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) | |||
264 | intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer)) | 267 | intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer)) |
265 | return -ENOMEM; | 268 | return -ENOMEM; |
266 | 269 | ||
267 | if (old_buffer) | ||
268 | auxtrace_buffer__drop_data(old_buffer); | ||
269 | |||
270 | if (buffer->use_data) { | 270 | if (buffer->use_data) { |
271 | b->len = buffer->use_size; | 271 | b->len = buffer->use_size; |
272 | b->buf = buffer->use_data; | 272 | b->buf = buffer->use_data; |
@@ -276,6 +276,16 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) | |||
276 | } | 276 | } |
277 | b->ref_timestamp = buffer->reference; | 277 | b->ref_timestamp = buffer->reference; |
278 | 278 | ||
279 | /* | ||
280 | * If in snapshot mode and the buffer has no usable data, get next | ||
281 | * buffer and again check overlap against old_buffer. | ||
282 | */ | ||
283 | if (ptq->pt->snapshot_mode && !b->len) | ||
284 | goto next; | ||
285 | |||
286 | if (old_buffer) | ||
287 | auxtrace_buffer__drop_data(old_buffer); | ||
288 | |||
279 | if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode && | 289 | if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode && |
280 | !buffer->consecutive)) { | 290 | !buffer->consecutive)) { |
281 | b->consecutive = false; | 291 | b->consecutive = false; |
@@ -477,7 +487,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | |||
477 | start_ip = *ip; | 487 | start_ip = *ip; |
478 | 488 | ||
479 | /* Load maps to ensure dso->is_64_bit has been updated */ | 489 | /* Load maps to ensure dso->is_64_bit has been updated */ |
480 | map__load(al.map, machine->symbol_filter); | 490 | map__load(al.map); |
481 | 491 | ||
482 | x86_64 = al.map->dso->is_64_bit; | 492 | x86_64 = al.map->dso->is_64_bit; |
483 | 493 | ||
@@ -541,6 +551,76 @@ out_no_cache: | |||
541 | return 0; | 551 | return 0; |
542 | } | 552 | } |
543 | 553 | ||
554 | static bool intel_pt_match_pgd_ip(struct intel_pt *pt, uint64_t ip, | ||
555 | uint64_t offset, const char *filename) | ||
556 | { | ||
557 | struct addr_filter *filt; | ||
558 | bool have_filter = false; | ||
559 | bool hit_tracestop = false; | ||
560 | bool hit_filter = false; | ||
561 | |||
562 | list_for_each_entry(filt, &pt->filts.head, list) { | ||
563 | if (filt->start) | ||
564 | have_filter = true; | ||
565 | |||
566 | if ((filename && !filt->filename) || | ||
567 | (!filename && filt->filename) || | ||
568 | (filename && strcmp(filename, filt->filename))) | ||
569 | continue; | ||
570 | |||
571 | if (!(offset >= filt->addr && offset < filt->addr + filt->size)) | ||
572 | continue; | ||
573 | |||
574 | intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s hit filter: %s offset %#"PRIx64" size %#"PRIx64"\n", | ||
575 | ip, offset, filename ? filename : "[kernel]", | ||
576 | filt->start ? "filter" : "stop", | ||
577 | filt->addr, filt->size); | ||
578 | |||
579 | if (filt->start) | ||
580 | hit_filter = true; | ||
581 | else | ||
582 | hit_tracestop = true; | ||
583 | } | ||
584 | |||
585 | if (!hit_tracestop && !hit_filter) | ||
586 | intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s is not in a filter region\n", | ||
587 | ip, offset, filename ? filename : "[kernel]"); | ||
588 | |||
589 | return hit_tracestop || (have_filter && !hit_filter); | ||
590 | } | ||
591 | |||
592 | static int __intel_pt_pgd_ip(uint64_t ip, void *data) | ||
593 | { | ||
594 | struct intel_pt_queue *ptq = data; | ||
595 | struct thread *thread; | ||
596 | struct addr_location al; | ||
597 | u8 cpumode; | ||
598 | u64 offset; | ||
599 | |||
600 | if (ip >= ptq->pt->kernel_start) | ||
601 | return intel_pt_match_pgd_ip(ptq->pt, ip, ip, NULL); | ||
602 | |||
603 | cpumode = PERF_RECORD_MISC_USER; | ||
604 | |||
605 | thread = ptq->thread; | ||
606 | if (!thread) | ||
607 | return -EINVAL; | ||
608 | |||
609 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al); | ||
610 | if (!al.map || !al.map->dso) | ||
611 | return -EINVAL; | ||
612 | |||
613 | offset = al.map->map_ip(al.map, ip); | ||
614 | |||
615 | return intel_pt_match_pgd_ip(ptq->pt, ip, offset, | ||
616 | al.map->dso->long_name); | ||
617 | } | ||
618 | |||
619 | static bool intel_pt_pgd_ip(uint64_t ip, void *data) | ||
620 | { | ||
621 | return __intel_pt_pgd_ip(ip, data) > 0; | ||
622 | } | ||
623 | |||
544 | static bool intel_pt_get_config(struct intel_pt *pt, | 624 | static bool intel_pt_get_config(struct intel_pt *pt, |
545 | struct perf_event_attr *attr, u64 *config) | 625 | struct perf_event_attr *attr, u64 *config) |
546 | { | 626 | { |
@@ -717,6 +797,9 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, | |||
717 | params.tsc_ctc_ratio_n = pt->tsc_ctc_ratio_n; | 797 | params.tsc_ctc_ratio_n = pt->tsc_ctc_ratio_n; |
718 | params.tsc_ctc_ratio_d = pt->tsc_ctc_ratio_d; | 798 | params.tsc_ctc_ratio_d = pt->tsc_ctc_ratio_d; |
719 | 799 | ||
800 | if (pt->filts.cnt > 0) | ||
801 | params.pgd_ip = intel_pt_pgd_ip; | ||
802 | |||
720 | if (pt->synth_opts.instructions) { | 803 | if (pt->synth_opts.instructions) { |
721 | if (pt->synth_opts.period) { | 804 | if (pt->synth_opts.period) { |
722 | switch (pt->synth_opts.period_type) { | 805 | switch (pt->synth_opts.period_type) { |
@@ -1294,7 +1377,7 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip) | |||
1294 | if (!map) | 1377 | if (!map) |
1295 | return 0; | 1378 | return 0; |
1296 | 1379 | ||
1297 | if (map__load(map, machine->symbol_filter)) | 1380 | if (map__load(map)) |
1298 | return 0; | 1381 | return 0; |
1299 | 1382 | ||
1300 | start = dso__first_symbol(map->dso, MAP__FUNCTION); | 1383 | start = dso__first_symbol(map->dso, MAP__FUNCTION); |
@@ -1767,6 +1850,8 @@ static void intel_pt_free(struct perf_session *session) | |||
1767 | intel_pt_free_events(session); | 1850 | intel_pt_free_events(session); |
1768 | session->auxtrace = NULL; | 1851 | session->auxtrace = NULL; |
1769 | thread__put(pt->unknown_thread); | 1852 | thread__put(pt->unknown_thread); |
1853 | addr_filters__exit(&pt->filts); | ||
1854 | zfree(&pt->filter); | ||
1770 | free(pt); | 1855 | free(pt); |
1771 | } | 1856 | } |
1772 | 1857 | ||
@@ -2016,6 +2101,8 @@ static const char * const intel_pt_info_fmts[] = { | |||
2016 | [INTEL_PT_TSC_CTC_N] = " TSC:CTC numerator %"PRIu64"\n", | 2101 | [INTEL_PT_TSC_CTC_N] = " TSC:CTC numerator %"PRIu64"\n", |
2017 | [INTEL_PT_TSC_CTC_D] = " TSC:CTC denominator %"PRIu64"\n", | 2102 | [INTEL_PT_TSC_CTC_D] = " TSC:CTC denominator %"PRIu64"\n", |
2018 | [INTEL_PT_CYC_BIT] = " CYC bit %#"PRIx64"\n", | 2103 | [INTEL_PT_CYC_BIT] = " CYC bit %#"PRIx64"\n", |
2104 | [INTEL_PT_MAX_NONTURBO_RATIO] = " Max non-turbo ratio %"PRIu64"\n", | ||
2105 | [INTEL_PT_FILTER_STR_LEN] = " Filter string len. %"PRIu64"\n", | ||
2019 | }; | 2106 | }; |
2020 | 2107 | ||
2021 | static void intel_pt_print_info(u64 *arr, int start, int finish) | 2108 | 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) | |||
2029 | fprintf(stdout, intel_pt_info_fmts[i], arr[i]); | 2116 | fprintf(stdout, intel_pt_info_fmts[i], arr[i]); |
2030 | } | 2117 | } |
2031 | 2118 | ||
2119 | static void intel_pt_print_info_str(const char *name, const char *str) | ||
2120 | { | ||
2121 | if (!dump_trace) | ||
2122 | return; | ||
2123 | |||
2124 | fprintf(stdout, " %-20s%s\n", name, str ? str : ""); | ||
2125 | } | ||
2126 | |||
2127 | static bool intel_pt_has(struct auxtrace_info_event *auxtrace_info, int pos) | ||
2128 | { | ||
2129 | return auxtrace_info->header.size >= | ||
2130 | sizeof(struct auxtrace_info_event) + (sizeof(u64) * (pos + 1)); | ||
2131 | } | ||
2132 | |||
2032 | int intel_pt_process_auxtrace_info(union perf_event *event, | 2133 | int intel_pt_process_auxtrace_info(union perf_event *event, |
2033 | struct perf_session *session) | 2134 | struct perf_session *session) |
2034 | { | 2135 | { |
2035 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; | 2136 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; |
2036 | size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS; | 2137 | size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS; |
2037 | struct intel_pt *pt; | 2138 | struct intel_pt *pt; |
2139 | void *info_end; | ||
2140 | u64 *info; | ||
2038 | int err; | 2141 | int err; |
2039 | 2142 | ||
2040 | if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + | 2143 | if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + |
@@ -2045,6 +2148,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2045 | if (!pt) | 2148 | if (!pt) |
2046 | return -ENOMEM; | 2149 | return -ENOMEM; |
2047 | 2150 | ||
2151 | addr_filters__init(&pt->filts); | ||
2152 | |||
2048 | perf_config(intel_pt_perf_config, pt); | 2153 | perf_config(intel_pt_perf_config, pt); |
2049 | 2154 | ||
2050 | err = auxtrace_queues__init(&pt->queues); | 2155 | err = auxtrace_queues__init(&pt->queues); |
@@ -2069,8 +2174,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2069 | intel_pt_print_info(&auxtrace_info->priv[0], INTEL_PT_PMU_TYPE, | 2174 | intel_pt_print_info(&auxtrace_info->priv[0], INTEL_PT_PMU_TYPE, |
2070 | INTEL_PT_PER_CPU_MMAPS); | 2175 | INTEL_PT_PER_CPU_MMAPS); |
2071 | 2176 | ||
2072 | if (auxtrace_info->header.size >= sizeof(struct auxtrace_info_event) + | 2177 | if (intel_pt_has(auxtrace_info, INTEL_PT_CYC_BIT)) { |
2073 | (sizeof(u64) * INTEL_PT_CYC_BIT)) { | ||
2074 | pt->mtc_bit = auxtrace_info->priv[INTEL_PT_MTC_BIT]; | 2178 | pt->mtc_bit = auxtrace_info->priv[INTEL_PT_MTC_BIT]; |
2075 | pt->mtc_freq_bits = auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS]; | 2179 | pt->mtc_freq_bits = auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS]; |
2076 | pt->tsc_ctc_ratio_n = auxtrace_info->priv[INTEL_PT_TSC_CTC_N]; | 2180 | 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, | |||
2080 | INTEL_PT_CYC_BIT); | 2184 | INTEL_PT_CYC_BIT); |
2081 | } | 2185 | } |
2082 | 2186 | ||
2187 | if (intel_pt_has(auxtrace_info, INTEL_PT_MAX_NONTURBO_RATIO)) { | ||
2188 | pt->max_non_turbo_ratio = | ||
2189 | auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO]; | ||
2190 | intel_pt_print_info(&auxtrace_info->priv[0], | ||
2191 | INTEL_PT_MAX_NONTURBO_RATIO, | ||
2192 | INTEL_PT_MAX_NONTURBO_RATIO); | ||
2193 | } | ||
2194 | |||
2195 | info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1; | ||
2196 | info_end = (void *)info + auxtrace_info->header.size; | ||
2197 | |||
2198 | if (intel_pt_has(auxtrace_info, INTEL_PT_FILTER_STR_LEN)) { | ||
2199 | size_t len; | ||
2200 | |||
2201 | len = auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN]; | ||
2202 | intel_pt_print_info(&auxtrace_info->priv[0], | ||
2203 | INTEL_PT_FILTER_STR_LEN, | ||
2204 | INTEL_PT_FILTER_STR_LEN); | ||
2205 | if (len) { | ||
2206 | const char *filter = (const char *)info; | ||
2207 | |||
2208 | len = roundup(len + 1, 8); | ||
2209 | info += len >> 3; | ||
2210 | if ((void *)info > info_end) { | ||
2211 | pr_err("%s: bad filter string length\n", __func__); | ||
2212 | err = -EINVAL; | ||
2213 | goto err_free_queues; | ||
2214 | } | ||
2215 | pt->filter = memdup(filter, len); | ||
2216 | if (!pt->filter) { | ||
2217 | err = -ENOMEM; | ||
2218 | goto err_free_queues; | ||
2219 | } | ||
2220 | if (session->header.needs_swap) | ||
2221 | mem_bswap_64(pt->filter, len); | ||
2222 | if (pt->filter[len - 1]) { | ||
2223 | pr_err("%s: filter string not null terminated\n", __func__); | ||
2224 | err = -EINVAL; | ||
2225 | goto err_free_queues; | ||
2226 | } | ||
2227 | err = addr_filters__parse_bare_filter(&pt->filts, | ||
2228 | filter); | ||
2229 | if (err) | ||
2230 | goto err_free_queues; | ||
2231 | } | ||
2232 | intel_pt_print_info_str("Filter string", pt->filter); | ||
2233 | } | ||
2234 | |||
2083 | pt->timeless_decoding = intel_pt_timeless_decoding(pt); | 2235 | pt->timeless_decoding = intel_pt_timeless_decoding(pt); |
2084 | pt->have_tsc = intel_pt_have_tsc(pt); | 2236 | pt->have_tsc = intel_pt_have_tsc(pt); |
2085 | pt->sampling_mode = false; | 2237 | pt->sampling_mode = false; |
@@ -2121,11 +2273,13 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2121 | pt->switch_evsel = intel_pt_find_sched_switch(session->evlist); | 2273 | pt->switch_evsel = intel_pt_find_sched_switch(session->evlist); |
2122 | if (!pt->switch_evsel) { | 2274 | if (!pt->switch_evsel) { |
2123 | pr_err("%s: missing sched_switch event\n", __func__); | 2275 | pr_err("%s: missing sched_switch event\n", __func__); |
2276 | err = -EINVAL; | ||
2124 | goto err_delete_thread; | 2277 | goto err_delete_thread; |
2125 | } | 2278 | } |
2126 | } else if (pt->have_sched_switch == 2 && | 2279 | } else if (pt->have_sched_switch == 2 && |
2127 | !intel_pt_find_switch(session->evlist)) { | 2280 | !intel_pt_find_switch(session->evlist)) { |
2128 | pr_err("%s: missing context_switch attribute flag\n", __func__); | 2281 | pr_err("%s: missing context_switch attribute flag\n", __func__); |
2282 | err = -EINVAL; | ||
2129 | goto err_delete_thread; | 2283 | goto err_delete_thread; |
2130 | } | 2284 | } |
2131 | 2285 | ||
@@ -2149,7 +2303,9 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2149 | if (pt->tc.time_mult) { | 2303 | if (pt->tc.time_mult) { |
2150 | u64 tsc_freq = intel_pt_ns_to_ticks(pt, 1000000000); | 2304 | u64 tsc_freq = intel_pt_ns_to_ticks(pt, 1000000000); |
2151 | 2305 | ||
2152 | pt->max_non_turbo_ratio = (tsc_freq + 50000000) / 100000000; | 2306 | if (!pt->max_non_turbo_ratio) |
2307 | pt->max_non_turbo_ratio = | ||
2308 | (tsc_freq + 50000000) / 100000000; | ||
2153 | intel_pt_log("TSC frequency %"PRIu64"\n", tsc_freq); | 2309 | intel_pt_log("TSC frequency %"PRIu64"\n", tsc_freq); |
2154 | intel_pt_log("Maximum non-turbo ratio %u\n", | 2310 | intel_pt_log("Maximum non-turbo ratio %u\n", |
2155 | pt->max_non_turbo_ratio); | 2311 | pt->max_non_turbo_ratio); |
@@ -2193,6 +2349,8 @@ err_free_queues: | |||
2193 | auxtrace_queues__free(&pt->queues); | 2349 | auxtrace_queues__free(&pt->queues); |
2194 | session->auxtrace = NULL; | 2350 | session->auxtrace = NULL; |
2195 | err_free: | 2351 | err_free: |
2352 | addr_filters__exit(&pt->filts); | ||
2353 | zfree(&pt->filter); | ||
2196 | free(pt); | 2354 | free(pt); |
2197 | return err; | 2355 | return err; |
2198 | } | 2356 | } |