diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index f76648fa6768..95e3ec1171ac 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -18,6 +18,7 @@ #include #include "auxtrace.h" +#include "callchain.h" #include "color.h" #include "cs-etm.h" #include "cs-etm-decoder/cs-etm-decoder.h" @@ -87,9 +88,11 @@ struct cs_etm_auxtrace { struct cs_etm_traceid_queue { u8 trace_chan_id; u64 period_instructions; + u64 kernel_start; union perf_event *event_buf; unsigned int br_stack_sz; struct branch_stack *last_branch; + struct ip_callchain *callchain; struct cs_etm_packet *prev_packet; struct cs_etm_packet *packet; struct cs_etm_packet_queue packet_queue; @@ -670,6 +673,15 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq, tidq->br_stack_sz = etm->synth_opts.last_branch_sz; } + if (etm->synth_opts.callchain) { + /* Add 1 to callchain_sz for callchain context */ + tidq->callchain = + zalloc(struct_size(tidq->callchain, ips, + etm->synth_opts.callchain_sz + 1)); + if (!tidq->callchain) + goto out_free; + } + tidq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE); if (!tidq->event_buf) goto out_free; @@ -677,6 +689,7 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq, return 0; out_free: + zfree(&tidq->callchain); zfree(&tidq->last_branch); zfree(&tidq->prev_packet); zfree(&tidq->packet); @@ -960,6 +973,7 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq) thread__zput(tidq->frontend_thread); thread__zput(tidq->decode_thread); zfree(&tidq->event_buf); + zfree(&tidq->callchain); zfree(&tidq->last_branch); zfree(&tidq->prev_packet); zfree(&tidq->packet); @@ -1602,6 +1616,26 @@ static void cs_etm__sample_branch_stack(struct cs_etm_auxtrace *etm, tidq->last_branch, tidq->br_stack_sz); sample->branch_stack = tidq->last_branch; } + + if (etm->synth_opts.callchain) { + if (tidq->kernel_start) + thread_stack__sample(tidq->frontend_thread, + tidq->packet->cpu, + tidq->callchain, + etm->synth_opts.callchain_sz + 1, + sample->ip, tidq->kernel_start); + else + /* + * Clear the callchain when the kernel start address is + * not available yet. The empty callchain can then be + * consumed by cs_etm__inject_event(). + */ + memset(tidq->callchain, 0, + struct_size(tidq->callchain, ips, + etm->synth_opts.callchain_sz + 1)); + + sample->callchain = tidq->callchain; + } } static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, @@ -1800,6 +1834,9 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm, attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; } + if (etm->synth_opts.callchain) + attr.sample_type |= PERF_SAMPLE_CALLCHAIN; + if (etm->synth_opts.instructions) { attr.config = PERF_COUNT_HW_INSTRUCTIONS; attr.sample_period = etm->synth_opts.period; @@ -1931,6 +1968,34 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, return 0; } +static int cs_etm__context(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq) +{ + ocsd_ex_level el = tidq->packet->el; + struct machine *machine; + int ret; + + machine = cs_etm__get_machine(etmq, el); + if (!machine) { + ret = -EINVAL; + goto err; + } + + tidq->kernel_start = machine__kernel_start(machine); + + ret = cs_etm__etmq_update_thread(etmq, el, tidq->packet->tid, + &tidq->frontend_thread); + if (ret) + goto err; + + return 0; + +err: + thread__zput(tidq->frontend_thread); + tidq->kernel_start = 0; + return ret; +} + static int cs_etm__exception(struct cs_etm_traceid_queue *tidq) { /* @@ -2531,9 +2596,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, * tracing the kernel the context packet will be emitted * between two ranges. */ - ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el, - tidq->packet->tid, - &tidq->frontend_thread); + ret = cs_etm__context(etmq, tidq); if (ret) goto out; break; @@ -3572,6 +3635,14 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END; + if (etm->synth_opts.callchain && !symbol_conf.use_callchain) { + symbol_conf.use_callchain = true; + if (callchain_register_param(&callchain_param) < 0) { + symbol_conf.use_callchain = false; + etm->synth_opts.callchain = false; + } + } + etm->session = session; etm->num_cpu = num_cpu; @@ -3623,9 +3694,11 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, } etm->use_thread_stack = etm->synth_opts.thread_stack || - etm->synth_opts.last_branch; + etm->synth_opts.last_branch || + etm->synth_opts.callchain; - etm->use_callchain = etm->synth_opts.thread_stack; + etm->use_callchain = etm->synth_opts.thread_stack || + etm->synth_opts.callchain; err = cs_etm__synth_events(etm, session); if (err)