diff options
author | Ingo Molnar <mingo@kernel.org> | 2015-09-29 09:43:46 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-09-29 09:43:46 +0200 |
commit | 9c17dbc6eb73bdd8a6aaea1baefd37ff78d86148 (patch) | |
tree | 5b303f98bc8898f26d2445138904f2fb14403a2d /tools/perf/util/intel-pt.c | |
parent | 18ab2cd3ee9d52dc64c5ae984146a261a328c4e8 (diff) | |
parent | e637d17757a10732fa5d573c18f20b3cd4d31245 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
- Accept a zero --itrace period, meaning "as often as possible". In the case
of Intel PT that is the same as a period of 1 and a unit of 'instructions'
(i.e. --itrace=i1i). (Adrian Hunter)
- Harmonize itrace's synthesized callchains with the existing --max-stack
tool option. (Adrian Hunter)
- Allow time to be displayed in nanoseconds in 'perf script'. (Adrian Hunter)
- Fix potential infinite loop when handling Intel PT timestamps. (Adrian Hunter)
- Slighly improve Intel PT debug logging. (Adrian Hunter)
- Warn when AUX data has been lost, just like when processing PERF_RECORD_LOST.
(Adrian Hunter)
- Further document export-to-postgresql.py script. (Adrian Hunter)
- Add option to synthesize branch stack from auxtrace data. (Adrian Hunter)
- Use equivalent logic to avoid using dso->kernel. (Arnaldo Carvalho de Melo)
- Show proper error messages when parsing bad terms for hw/sw events. (He Kuang)
- Tracepoint event parsing improvements. (He Kuang)
- Store tracing mountpoint for better error message. (Jiri Olsa)
- Add fixdep to tools/build, bringing it closer to the kernel counterpart, from
where it is being lifted. (Jiri Olsa)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/util/intel-pt.c')
-rw-r--r-- | tools/perf/util/intel-pt.c | 135 |
1 files changed, 132 insertions, 3 deletions
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 38942e1eac8f..03ff072b5993 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -22,6 +22,7 @@ #include "../perf.h" #include "session.h" #include "machine.h" +#include "sort.h" #include "tool.h" #include "event.h" #include "evlist.h" @@ -63,6 +64,7 @@ struct intel_pt { bool data_queued; bool est_tsc; bool sync_switch; + bool mispred_all; int have_sched_switch; u32 pmu_type; u64 kernel_start; @@ -115,6 +117,9 @@ struct intel_pt_queue { void *decoder; const struct intel_pt_state *state; struct ip_callchain *chain; + struct branch_stack *last_branch; + struct branch_stack *last_branch_rb; + size_t last_branch_pos; union perf_event *event_buf; bool on_heap; bool stop; @@ -675,6 +680,19 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, goto out_free; } + if (pt->synth_opts.last_branch) { + size_t sz = sizeof(struct branch_stack); + + sz += pt->synth_opts.last_branch_sz * + sizeof(struct branch_entry); + ptq->last_branch = zalloc(sz); + if (!ptq->last_branch) + goto out_free; + ptq->last_branch_rb = zalloc(sz); + if (!ptq->last_branch_rb) + goto out_free; + } + ptq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE); if (!ptq->event_buf) goto out_free; @@ -720,7 +738,7 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, if (!params.period) { params.period_type = INTEL_PT_PERIOD_INSTRUCTIONS; - params.period = 1000; + params.period = 1; } } @@ -732,6 +750,8 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, out_free: zfree(&ptq->event_buf); + zfree(&ptq->last_branch); + zfree(&ptq->last_branch_rb); zfree(&ptq->chain); free(ptq); return NULL; @@ -746,6 +766,8 @@ static void intel_pt_free_queue(void *priv) thread__zput(ptq->thread); intel_pt_decoder_free(ptq->decoder); zfree(&ptq->event_buf); + zfree(&ptq->last_branch); + zfree(&ptq->last_branch_rb); zfree(&ptq->chain); free(ptq); } @@ -876,6 +898,58 @@ static int intel_pt_setup_queues(struct intel_pt *pt) return 0; } +static inline void intel_pt_copy_last_branch_rb(struct intel_pt_queue *ptq) +{ + struct branch_stack *bs_src = ptq->last_branch_rb; + struct branch_stack *bs_dst = ptq->last_branch; + size_t nr = 0; + + bs_dst->nr = bs_src->nr; + + if (!bs_src->nr) + return; + + nr = ptq->pt->synth_opts.last_branch_sz - ptq->last_branch_pos; + memcpy(&bs_dst->entries[0], + &bs_src->entries[ptq->last_branch_pos], + sizeof(struct branch_entry) * nr); + + if (bs_src->nr >= ptq->pt->synth_opts.last_branch_sz) { + memcpy(&bs_dst->entries[nr], + &bs_src->entries[0], + sizeof(struct branch_entry) * ptq->last_branch_pos); + } +} + +static inline void intel_pt_reset_last_branch_rb(struct intel_pt_queue *ptq) +{ + ptq->last_branch_pos = 0; + ptq->last_branch_rb->nr = 0; +} + +static void intel_pt_update_last_branch_rb(struct intel_pt_queue *ptq) +{ + const struct intel_pt_state *state = ptq->state; + struct branch_stack *bs = ptq->last_branch_rb; + struct branch_entry *be; + + if (!ptq->last_branch_pos) + ptq->last_branch_pos = ptq->pt->synth_opts.last_branch_sz; + + ptq->last_branch_pos -= 1; + + be = &bs->entries[ptq->last_branch_pos]; + be->from = state->from_ip; + be->to = state->to_ip; + be->flags.abort = !!(state->flags & INTEL_PT_ABORT_TX); + be->flags.in_tx = !!(state->flags & INTEL_PT_IN_TX); + /* No support for mispredict */ + be->flags.mispred = ptq->pt->mispred_all; + + if (bs->nr < ptq->pt->synth_opts.last_branch_sz) + bs->nr += 1; +} + static int intel_pt_inject_event(union perf_event *event, struct perf_sample *sample, u64 type, bool swapped) @@ -890,6 +964,13 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) struct intel_pt *pt = ptq->pt; union perf_event *event = ptq->event_buf; struct perf_sample sample = { .ip = 0, }; + struct dummy_branch_stack { + u64 nr; + struct branch_entry entries; + } dummy_bs; + + if (pt->branches_filter && !(pt->branches_filter & ptq->flags)) + return 0; event->sample.header.type = PERF_RECORD_SAMPLE; event->sample.header.misc = PERF_RECORD_MISC_USER; @@ -909,8 +990,20 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; - if (pt->branches_filter && !(pt->branches_filter & ptq->flags)) - return 0; + /* + * perf report cannot handle events without a branch stack when using + * SORT_MODE__BRANCH so make a dummy one. + */ + if (pt->synth_opts.last_branch && sort__mode == SORT_MODE__BRANCH) { + dummy_bs = (struct dummy_branch_stack){ + .nr = 1, + .entries = { + .from = sample.ip, + .to = sample.addr, + }, + }; + sample.branch_stack = (struct branch_stack *)&dummy_bs; + } if (pt->synth_opts.inject) { ret = intel_pt_inject_event(event, &sample, @@ -961,6 +1054,11 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) sample.callchain = ptq->chain; } + if (pt->synth_opts.last_branch) { + intel_pt_copy_last_branch_rb(ptq); + sample.branch_stack = ptq->last_branch; + } + if (pt->synth_opts.inject) { ret = intel_pt_inject_event(event, &sample, pt->instructions_sample_type, @@ -974,6 +1072,9 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) pr_err("Intel Processor Trace: failed to deliver instruction event, error %d\n", ret); + if (pt->synth_opts.last_branch) + intel_pt_reset_last_branch_rb(ptq); + return ret; } @@ -1008,6 +1109,11 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) sample.callchain = ptq->chain; } + if (pt->synth_opts.last_branch) { + intel_pt_copy_last_branch_rb(ptq); + sample.branch_stack = ptq->last_branch; + } + if (pt->synth_opts.inject) { ret = intel_pt_inject_event(event, &sample, pt->transactions_sample_type, @@ -1021,6 +1127,9 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) pr_err("Intel Processor Trace: failed to deliver transaction event, error %d\n", ret); + if (pt->synth_opts.callchain) + intel_pt_reset_last_branch_rb(ptq); + return ret; } @@ -1116,6 +1225,9 @@ static int intel_pt_sample(struct intel_pt_queue *ptq) return err; } + if (pt->synth_opts.last_branch) + intel_pt_update_last_branch_rb(ptq); + if (!pt->sync_switch) return 0; @@ -1763,6 +1875,8 @@ static int intel_pt_synth_events(struct intel_pt *pt, pt->instructions_sample_period = attr.sample_period; if (pt->synth_opts.callchain) attr.sample_type |= PERF_SAMPLE_CALLCHAIN; + if (pt->synth_opts.last_branch) + attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; pr_debug("Synthesizing 'instructions' event with id %" PRIu64 " sample type %#" PRIx64 "\n", id, (u64)attr.sample_type); err = intel_pt_synth_event(session, &attr, id); @@ -1782,6 +1896,8 @@ static int intel_pt_synth_events(struct intel_pt *pt, attr.sample_period = 1; if (pt->synth_opts.callchain) attr.sample_type |= PERF_SAMPLE_CALLCHAIN; + if (pt->synth_opts.last_branch) + attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; pr_debug("Synthesizing 'transactions' event with id %" PRIu64 " sample type %#" PRIx64 "\n", id, (u64)attr.sample_type); err = intel_pt_synth_event(session, &attr, id); @@ -1808,6 +1924,7 @@ static int intel_pt_synth_events(struct intel_pt *pt, attr.sample_period = 1; attr.sample_type |= PERF_SAMPLE_ADDR; attr.sample_type &= ~(u64)PERF_SAMPLE_CALLCHAIN; + attr.sample_type &= ~(u64)PERF_SAMPLE_BRANCH_STACK; pr_debug("Synthesizing 'branches' event with id %" PRIu64 " sample type %#" PRIx64 "\n", id, (u64)attr.sample_type); err = intel_pt_synth_event(session, &attr, id); @@ -1852,6 +1969,16 @@ static bool intel_pt_find_switch(struct perf_evlist *evlist) return false; } +static int intel_pt_perf_config(const char *var, const char *value, void *data) +{ + struct intel_pt *pt = data; + + if (!strcmp(var, "intel-pt.mispred-all")) + pt->mispred_all = perf_config_bool(var, value); + + return 0; +} + static const char * const intel_pt_info_fmts[] = { [INTEL_PT_PMU_TYPE] = " PMU Type %"PRId64"\n", [INTEL_PT_TIME_SHIFT] = " Time Shift %"PRIu64"\n", @@ -1896,6 +2023,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event, if (!pt) return -ENOMEM; + perf_config(intel_pt_perf_config, pt); + err = auxtrace_queues__init(&pt->queues); if (err) goto err_free; |