From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 74CE0606A31E for ; Tue, 20 Oct 2026 14:28:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=wKF/jY6z; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=L22px/Bv DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: Content-Type:In-Reply-To:From:References:Cc:To:Subject:MIME-Version:Date: Message-ID:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=zqiNo29U5F/POgSeiY+9/aC7kFsroceZdJdpmz64yjM=; b=wKF/jY6zQb5JQMKa+MRMsVfiph qFOfaIngOx8n7kaqgA7W7fYBvOh5UWfXRz5mSLrxhaX0dmlMulUbvMpH4uSN4h3Gn/nsIsGGM7wyr zR2+979lb6Ngkda6KUEauymBacAy8lqAh3N096clCtuJ2GSG6Sl1igM/YoHeFu14K1UmWOS+9Cjjr 2m5Xf1kGgGxkNWcG6U0EEADfXyKzrzJ9opvp5ghh1Tl42lkfQfq2pmnCMRSSYzUwqYz7Qo+eNNc2F rJyQlV0Tw6c2MU8Xu+hufdTDOzuBL5x/hWalwb0wMQCJVfLZinAd/5Ve94gH8S1tOPj3Te8ZRBtxk a3IUs9HA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1xJAbI-00000009teP-0RSH; Tue, 20 Oct 2026 14:14:00 +0000 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1xJAbF-00000009tct-12Ht for linux-arm-kernel@lists.infradead.org; Tue, 20 Oct 2026 14:13:58 +0000 Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-4a1d484b4a9so42021085e9.0 for ; Tue, 20 Oct 2026 07:13:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1792505635; x=1793110435; darn=lists.infradead.org; h=content-transfer-encoding:content-type:in-reply-to:from :content-language:references:cc:to:subject:user-agent:mime-version :date:message-id:from:to:cc:subject:date:message-id:reply-to :content-type; bh=zqiNo29U5F/POgSeiY+9/aC7kFsroceZdJdpmz64yjM=; b=L22px/BvIYfuKyD0GPFd96DDKgogPPGNBe8f9TLVGCkMPzfYtfhjZ21nZ8E5Uu9RAt wY84xlTVaMfsuqPC4XNcR0+TsvThci9He70oifQMaOQwLkhJd4Z+qxFbWgNZuOQLY1Qr s2SYkrUKLRR07hM1iHYjX5UCXBUNtOKJIbhFN8jXTlszo0N6TNe8knA1V8PtWdJTOx2b x+EJAnyCL89AtG6pIt6X8gNd4rnF94R/xkFTReD6xQP2VxqcMYPA2EGAo0AA5DE3wJv8 CyDp3l1UsPeumzqiD0KopAAArwkiMN+UPUIshhpDaiQD4jhpIKTLAroAULQQn2YKQEks sKNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792505635; x=1793110435; h=content-transfer-encoding:content-type:in-reply-to:from :content-language:references:cc:to:subject:user-agent:mime-version :date:message-id:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=zqiNo29U5F/POgSeiY+9/aC7kFsroceZdJdpmz64yjM=; b=CJQcOmLZmrMtcqzaJZ02dhqk1k/UJmniJQw3ZvqEFvPPAHRMW1xEKsdfBFyNQh20Dr gt1N9Z7U0aHuv32ejQE6gQMOa+l2hS/HMGLlhEBOG6qn4Cknxq8k3OTYuvBgh4zYiOyI M29BsmiVrRigQeney57DgIXR84MTvoYg/R+uP96E0ouFGgSCpEk9j2iRiNt3lCrqNPwa HswSwtuPBYE2UKltPnDexA63u8kTWUA4tI+zTtC1bPUCOsZf6lwTgr2/+pK0Uvn3wDYX bujnKh3mkmTNEMdRMyUu5/2NriNmXDGZ5TVRJ1RLrW26Sy7mpkXmKYW+aW08n/2RacVf V1iA== X-Forwarded-Encrypted: i=1; AKwUvBzGE8T59mRFlejZOL0+xrW8nMbnsiyTb490TyV8Mdyd8NUr6w4l70sSCyEwug2rMkGg3Q5BatCWOlJmSsHDMaCZ@lists.infradead.org X-Gm-Message-State: AFq9FYJq8bW8qLhhygvfr25QJRHVUDh+KwPemINYQskYhwBFFx81fo+I iYago9yGb7O8f2QmI31bvM4iucOjUd4xYDfSF0j+E3eXrvy7t6Nci45QGL/1qL/vlyo= X-Gm-Gg: AYBFou2KoPYKUvO25Gd3m446ttcgP30DkA8dNibzac+ghw0CTLT7TrQHRiLEjLUFcuu rBbI9Okw6jII3m4SbdCajEEdKie9L7iwQzWbZsafIMDsc5UGA4n6m1y0k0Tagbz8Cp0p3rT7emA ZREWHQkAhdOjvaJHu/zILDsmYyC32DzIkGiIXFLQdRPHgBg/m423RjG2li6yoHFdu2NyO13sYo0 6L9w6JA8oMAuyClfHW2Ovng5k17y5DEozCYvpnmC8L8OMl4nqB/aikIYG9Ft/Ko0Us3mPEdB1G/ saA2diU+CMwQ0gsxLospD4n4Yed8vdvxRo/EkYyA2gR5QwBPK4v3acm+X5AaSYzWNVo6+lCRN+4 OFPF9HMlnsfYFZjBuCxeHZ5ivKyypdX4s03IRnWUr3A5k7yFnkJwAwwLb+gQfEbnSzbPzr/v08v 15BNJUtes/0O03YyRZwq2WkwaWReismOZ6VrdX01bR9H0dXtqsvhv4MJtatQMYoYLgpVCyXTZBR wGyO6vPdUqROw== X-Received: by 2002:a05:600c:8709:b0:4a1:83f0:7c91 with SMTP id 5b1f17b1804b1-4a1d76255f6mr203914945e9.18.1792505632904; Tue, 20 Oct 2026 07:13:52 -0700 (PDT) Received: from [192.168.1.3] ([37.18.141.193]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4a1cca30aa5sm40054275e9.2.2026.10.20.07.13.51 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 20 Oct 2026 07:13:52 -0700 (PDT) Message-ID: <59264732-a4b3-4fd0-a0fc-7e2fc3f73d60@linaro.org> Date: Tue, 20 Oct 2026 15:13:50 +0100 X-Archived-By: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH 23/25] perf cs-etm: Decode AUX samples into callchains and branch stacks To: Leo Yan Cc: Ingo Molnar , linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, coresight@lists.linaro.org, linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Ian Rogers , Adrian Hunter , Suzuki K Poulose , Mike Leach , Anshuman Khandual , Yeoreum Yun , Jonathan Corbet , Suyash Mahar , Amir Ayupov References: <20260915-arm_cs_support_aux_sample-v1-0-35cf6f297d20@arm.com> <20260915-arm_cs_support_aux_sample-v1-23-35cf6f297d20@arm.com> Content-Language: en-US From: James Clark In-Reply-To: <20260915-arm_cs_support_aux_sample-v1-23-35cf6f297d20@arm.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20261020_071357_494494_D55BC021 X-CRM114-Status: GOOD ( 37.29 ) X-Spam-Score: -2.1 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" X-Spam-Status: No, score=-17.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 On 15/09/2026 16:49, Leo Yan wrote: > Decode raw AUX trace embedded in PERF_SAMPLE_AUX and attach missing > callchains and branch stacks to the owning PMU samples. > > Select the decoder by the sampled CPU and initialize its thread context > from the sample's PID and TID. Queue each payload temporarily for the > shared timeless decoder, clearing packet queues and flushing thread > stacks before decoding an independent window. The embedded trace already > belongs to the sample, so timestamp correlation is not needed. > > Extend cs_etm__save_sample_history() to collect callchains as well as > branch stacks after decoding. Support --itrace=G and --itrace=L while > preserving recorded callchains and branch stacks. > > Record CPU ID on AUX sampling events so each sample selects the correct > decoder. > > Assisted-by: Codex:gpt-6 > Signed-off-by: Leo Yan > --- > tools/perf/arch/arm/util/cs-etm.c | 6 + > tools/perf/util/cs-etm.c | 308 +++++++++++++++++++++++++++++++++----- > 2 files changed, 274 insertions(+), 40 deletions(-) > > diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c > index d2861d66a6612ea213066258672ba41769e578cb..710887daad0e4cbb5f48a45aa396c7bbab4c7ead 100644 > --- a/tools/perf/arch/arm/util/cs-etm.c > +++ b/tools/perf/arch/arm/util/cs-etm.c > @@ -450,6 +450,12 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, > */ > evsel__set_sample_bit(cs_etm_evsel, CPU); > > + /* Raw AUX samples need the sampling CPU to select the trace decoder. */ > + evlist__for_each_entry(evlist, evsel) { > + if (evsel->core.attr.aux_sample_size) > + evsel__set_sample_bit(evsel, CPU); > + } > + > /* > * Also the case of per-cpu mmaps, need the contextID in order to be notified > * when a context switch happened. > diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c > index 815a22cedc49e9281b7087f01355bb71b5e41c57..8a1b532df0d3f45fbec85b32ade9bc5ce5b58c98 100644 > --- a/tools/perf/util/cs-etm.c > +++ b/tools/perf/util/cs-etm.c > @@ -66,6 +66,7 @@ struct cs_etm_auxtrace { > */ > bool per_thread_decoding; > bool snapshot_mode; > + bool sampling_mode; > bool data_queued; > bool has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */ > bool use_thread_stack; > @@ -77,6 +78,7 @@ struct cs_etm_auxtrace { > /* Internal reconstruction depth, see cs_etm__br_stack_init() */ > unsigned int br_stack_sz_plus; > struct branch_stack *br_stack; > + struct ip_callchain *chain; > u64 latest_kernel_timestamp; > u32 auxtrace_type; > u32 branches_filter; > @@ -804,7 +806,8 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm, > struct cs_etm_packet *tmp; > > if (etm->synth_opts.branches || etm->synth_opts.last_branch || > - etm->synth_opts.add_last_branch || etm->synth_opts.instructions) { > + etm->synth_opts.add_last_branch || etm->synth_opts.add_callchain || > + etm->synth_opts.instructions) { > /* > * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for > * the next incoming packet. > @@ -961,7 +964,7 @@ static int cs_etm__flush_events(struct perf_session *session, > auxtrace); > int ret; > > - if (dump_trace) > + if (dump_trace || etm->sampling_mode) > return 0; > > if (!tool->ordered_events) > @@ -1077,6 +1080,7 @@ static void cs_etm__free(struct perf_session *session) > > zfree(&aux->metadata); > zfree(&aux->br_stack); > + zfree(&aux->chain); > zfree(&aux); > } > > @@ -1102,6 +1106,13 @@ static struct machine *cs_etm__get_machine(struct cs_etm_queue *etmq, > if (pid_fmt == CS_ETM_PIDFMT_CTXTID) > return &etmq->etm->session->machines.host; > > + /* > + * AUX samples without context IDs use the owning host sample's PID/TID. > + * Keep EL1 trace in that host context as well. > + */ > + if (etmq->etm->sampling_mode && pid_fmt == CS_ETM_PIDFMT_NONE) > + return &etmq->etm->session->machines.host; > + This is probably not necessary to change if we base off of the per-thread decoding patches. "[PATCH 01/14] perf cs-etm: Fix nVHE per-thread decoding" already sets host if "perf_guest" isn't set. And I don't see a need to tie machine detection into aux sampling, it should affect the actual decoding as little as possible, it's just another way to gather trace. > /* > * Not perfect, but otherwise assume anything in EL1 is the default > * guest, and everything else is the host. Distinguishing between guest > @@ -1615,7 +1626,7 @@ static void cs_etm__add_stack_event(struct cs_etm_queue *etmq, > int size; > > if (!etm->synth_opts.branches && !etm->synth_opts.instructions && > - !etm->synth_opts.add_last_branch) > + !etm->synth_opts.add_last_branch && !etm->synth_opts.add_callchain) > return; > > if (!cs_etm__packet_has_taken_branch(tidq->prev_packet)) > @@ -2059,10 +2070,17 @@ static int cs_etm__save_sample_history(struct cs_etm_auxtrace *etm, > return -ENOMEM; > > /* Consume branch history so later samples cannot reuse the same window. */ > - thread_stack__br_sample_late(thread, sample->cpu, etm->br_stack, > - etm->br_stack_sz, sample->ip, > - machine__kernel_start(machine)); > - thread_stack__br_stack_consume(thread, sample->cpu); > + if (etm->synth_opts.add_last_branch && !sample->branch_stack) { > + thread_stack__br_sample_late(thread, sample->cpu, etm->br_stack, > + etm->br_stack_sz, sample->ip, > + machine__kernel_start(machine)); > + thread_stack__br_stack_consume(thread, sample->cpu); > + } > + > + if (etm->synth_opts.add_callchain && !sample->callchain) > + thread_stack__sample_late(thread, sample->cpu, etm->chain, > + etm->synth_opts.callchain_sz + 1, > + sample->ip, machine__kernel_start(machine)); > > thread__put(thread); > return 0; > @@ -2187,6 +2205,13 @@ static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq) > cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID); > break; > case CS_ETM_PIDFMT_NONE: > + /* > + * AUX samples provide PID/TID via PERF_SAMPLE_TID, so flush host > + * stacks even without traced context IDs. > + */ > + if (etmq->etm->sampling_mode) > + cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID); > + break; > default: > break; > > @@ -3064,6 +3089,61 @@ static bool cs_etm__tracing_kernel(struct cs_etm_auxtrace *etm, > return false; > } > > +static bool cs_etm__sampling_mode(struct perf_session *session) > +{ > + struct evsel *evsel; > + > + evlist__for_each_entry(session->evlist, evsel) { > + if ((evsel->core.attr.sample_type & PERF_SAMPLE_AUX) && > + evsel->core.attr.aux_sample_size) > + return true; > + } > + return false; > +} > + > +static int cs_etm__aux_sample_init(struct cs_etm_auxtrace *etm) > +{ > + struct evsel *evsel; > + > + if (!etm->sampling_mode) { > + if (etm->synth_opts.add_callchain) { > + pr_err("CS ETM: --itrace=G requires AUX samples\n"); > + return -EINVAL; > + } > + > + return 0; > + } > + > + evlist__for_each_entry(etm->session->evlist, evsel) { > + u64 type = evsel->core.attr.sample_type; > + u64 required = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_CPU; > + > + if (!(type & PERF_SAMPLE_AUX)) > + continue; > + > + if ((type & required) != required) { > + pr_err("CS ETM: AUX samples require IP, TID and CPU\n"); > + return -EINVAL; > + } > + > + /* > + * Synthesize the callchain from AUX trace when it is not > + * provided by the PMU sample. > + */ > + if (etm->synth_opts.add_callchain && !(type & PERF_SAMPLE_CALLCHAIN)) > + evsel->synth_sample_type |= PERF_SAMPLE_CALLCHAIN; > + } > + > + if (etm->synth_opts.add_callchain) { > + etm->chain = zalloc(struct_size(etm->chain, ips, > + etm->synth_opts.callchain_sz + 1)); > + if (!etm->chain) > + return -ENOMEM; > + } > + > + return 0; > +} > + > static int cs_etm__br_stack_init(struct cs_etm_auxtrace *etm, > struct perf_session *session) > { > @@ -3071,10 +3151,11 @@ static int cs_etm__br_stack_init(struct cs_etm_auxtrace *etm, > > evlist__for_each_entry(session->evlist, evsel) { > /* > - * Only timestamped events can be matched against the decoded > - * trace, so do not advertise a branch stack on any other. > + * AUX samples own their trace window. Other samples need a > + * timestamp to match against the decoded trace. > */ > - if (!(evsel->core.attr.sample_type & PERF_SAMPLE_TIME)) > + if (!(evsel->core.attr.sample_type & > + (etm->sampling_mode ? PERF_SAMPLE_AUX : PERF_SAMPLE_TIME))) > continue; > if (!(evsel->core.attr.sample_type & PERF_SAMPLE_BRANCH_STACK)) > evsel->synth_sample_type |= PERF_SAMPLE_BRANCH_STACK; > @@ -3101,9 +3182,85 @@ static int cs_etm__br_stack_init(struct cs_etm_auxtrace *etm, > return 0; > } > > +static int cs_etm__set_sample_context(struct cs_etm_queue *etmq, > + const struct perf_sample *sample) > +{ > + struct machine *machine = &etmq->etm->session->machines.host; > + struct cs_etm_traceid_queue *tidq; > + u64 *metadata = get_cpu_data(etmq->etm, sample->cpu); > + struct thread *thread; > + u8 trace_id; > + int ret; > + > + if (!metadata) > + return -EINVAL; > + > + ret = cs_etm__metadata_get_trace_id(&trace_id, metadata); > + if (ret) > + return ret; > + > + tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_id); > + if (!tidq) > + return -ENOMEM; > + > + thread = machine__findnew_thread(machine, sample->pid, sample->tid); > + if (!thread) > + return -ENOMEM; > + > + tidq->kernel_start = machine__kernel_start(machine); > + tidq->decode_el = ocsd_EL_unknown; > + thread__put(tidq->decode_thread); > + thread__put(tidq->frontend_thread); > + /* Thread already holds a reference from machine__findnew_thread() */ > + tidq->decode_thread = thread; > + tidq->frontend_thread = thread__get(thread); > + > + return 0; > +} > + > +/* Consume the queued AUX window while its owning sample is still available. */ > +static int cs_etm__process_aux_sample(struct cs_etm_auxtrace *etm, > + struct perf_sample *sample) > +{ > + struct cs_etm_queue *etmq = cs_etm__get_queue(etm, sample->cpu); > + struct auxtrace_buffer buffer = { > + .data = sample->aux_sample.data, > + .size = sample->aux_sample.size, > + .pid = sample->pid, > + .tid = sample->tid, > + .cpu = { sample->cpu }, > + }; > + struct auxtrace_queue *queue; > + int ret; > + > + if (!etmq || !etmq->decoder) > + return -EINVAL; > + > + queue = &etm->queues.queue_array[etmq->queue_nr]; > + if (!list_empty(&queue->head) || etmq->buffer) > + return -EINVAL; > + > + ret = cs_etm__set_sample_context(etmq, sample); > + if (ret) > + return ret; > + > + cs_etm__clear_all_packet_queues(etmq); > + > + buffer.buffer_nr = etm->queues.next_buffer_nr++; > + list_add_tail(&buffer.list, &queue->head); > + ret = cs_etm__run_timeless_decoder(etmq); > + > + /* The buffer is borrowed for this decode only. */ > + list_del_init(&buffer.list); > + etmq->buffer = NULL; > + etmq->buf = NULL; > + etmq->buf_len = 0; > + return ret; > +} > + > /* > - * Add decoded branch history to an existing sample. The sample keeps its own > - * ip, callchain and event identity; only an absent branch stack is filled in. > + * Decode the trace belonging to this sample and fill in missing history. > + * The sample keeps its IP, event identity and any recorded stacks. > * A user-only PMU sample can have a zero IP when the interrupt skids into > * the kernel; its thread and trace window still identify valid history. > */ > @@ -3112,25 +3269,35 @@ static int cs_etm__process_sample(struct cs_etm_auxtrace *etm, > { > int err; > > - if (!etm->synth_opts.add_last_branch || sample->branch_stack || > - !sample->time || sample->time == (u64)-1) > + if ((!etm->synth_opts.add_last_branch || sample->branch_stack) && > + (!etm->synth_opts.add_callchain || sample->callchain)) > return 0; > > - /* Adding branch history to existing samples supports the host only */ > + /* Adding history to existing samples supports the host only */ > if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL || > sample->cpumode == PERF_RECORD_MISC_GUEST_USER) > return 0; > > - err = cs_etm__update_queues(etm); > - if (err) > - return err; > + if (etm->sampling_mode) { > + if (!sample->aux_sample.size) > + return 0; > > - /* > - * Decode every queue up to this sample's time. Afterwards the thread > - * stack holds the branches that executed before the sample, and > - * nothing that executed after it. > - */ > - err = cs_etm__process_timestamped_queues(etm, sample->time); > + err = cs_etm__process_aux_sample(etm, sample); > + } else { > + if (!sample->time || sample->time == (u64)-1) > + return 0; > + > + err = cs_etm__update_queues(etm); > + if (err) > + return err; > + > + /* > + * Decode every queue up to this sample's time. Afterwards the > + * thread stack holds the branches that executed before the > + * sample, and nothing that executed after it. > + */ > + err = cs_etm__process_timestamped_queues(etm, sample->time); > + } > if (err) > return err; > > @@ -3138,9 +3305,16 @@ static int cs_etm__process_sample(struct cs_etm_auxtrace *etm, > if (err) > return err; > > - if (etm->br_stack->nr) > + if (etm->synth_opts.add_last_branch && !sample->branch_stack && etm->br_stack->nr) > sample->branch_stack = etm->br_stack; > > + if (etm->synth_opts.add_callchain && !sample->callchain) { > + /* An empty history produces only a context marker and sample IP */ > + if (etm->chain->nr > 2 || > + (etm->chain->nr == 2 && etm->chain->ips[1] != sample->ip)) > + sample->callchain = etm->chain; > + } > + > return 0; > } > > @@ -3225,6 +3399,10 @@ static int cs_etm__process_auxtrace_event(struct perf_session *session, > struct cs_etm_auxtrace *etm = container_of(session->auxtrace, > struct cs_etm_auxtrace, > auxtrace); > + > + if (etm->sampling_mode) > + return 0; > + > if (!etm->data_queued) { > struct auxtrace_buffer *buffer; > off_t data_offset; > @@ -3529,14 +3707,28 @@ static int cs_etm__queue_aux_records_cb(struct perf_session *session, union perf > return ret; > } > > -static int cs_etm__queue_aux_records(struct perf_session *session) > +static int cs_etm__prepare_auxtrace_queues(struct cs_etm_auxtrace *etm, > + struct perf_session *session) > { > - struct auxtrace_index *index = list_first_entry_or_null(&session->auxtrace_index, > - struct auxtrace_index, list); > - if (index && index->nr > 0) > - return perf_session__peek_events(session, session->header.data_offset, > - session->header.data_size, > - cs_etm__queue_aux_records_cb, NULL); > + struct auxtrace_index *index; > + unsigned int i; > + > + /* > + * AUX samples have no AUX record to describe the formatter framing. > + * Since it is only supported by TRBE, the trace is always unformatted. > + */ > + if (etm->sampling_mode) { > + for (i = 0; i < etm->queues.nr_queues; i++) { > + struct cs_etm_queue *etmq = etm->queues.queue_array[i].priv; > + > + etmq->format = UNFORMATTED; > + } > + > + return 0; > + } > + > + index = list_first_entry_or_null(&session->auxtrace_index, > + struct auxtrace_index, list); > > /* > * We would get here if there are no entries in the index (either no auxtrace > @@ -3546,7 +3738,12 @@ static int cs_etm__queue_aux_records(struct perf_session *session) > * > * In that scenario, buffers will not be split by AUX records. > */ > - return 0; > + if (!index || index->nr <= 0) > + return 0; > + > + return perf_session__peek_events(session, session->header.data_offset, > + session->header.data_size, > + cs_etm__queue_aux_records_cb, NULL); > } > > #define HAS_PARAM(j, type, param) (metadata[(j)][CS_ETM_NR_TRC_PARAMS] <= \ > @@ -3622,6 +3819,11 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) > if (decoders == 0) > return 0; > > + if (etmq->etm->sampling_mode && decoders != 1) { > + pr_err("CS ETM Trace: AUX samples require a per-CPU raw trace source\n"); > + return -EINVAL; > + } > + > /* > * Each queue can only contain data from one CPU when unformatted, so only one decoder is > * needed. > @@ -3679,11 +3881,12 @@ static int cs_etm__create_decoders(struct cs_etm_auxtrace *etm) > int ret; > > /* > - * Don't create decoders for empty queues, mainly because > - * etmq->format is unknown for empty queues. > + * AUX sample buffers are queued when their samples are processed, > + * so create their decoders even though the queues are still empty. > + * Other empty queues have no known format or data to decode. > */ > assert(empty || etmq->format != UNSET); > - if (empty) > + if (empty && !etm->sampling_mode) > continue; > > ret = cs_etm__create_queue_decoders(etmq); > @@ -3818,6 +4021,19 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, > etm->synth_opts.thread_stack = session->itrace_synth_opts->thread_stack; > } > > + etm->sampling_mode = cs_etm__sampling_mode(session); > + if (etm->sampling_mode) { > + /* AUX windows augment their owning samples, without synthesizing events. */ > + etm->synth_opts.instructions = false; > + etm->synth_opts.branches = false; > + etm->synth_opts.callchain = false; > + etm->synth_opts.last_branch = false; > + if (!session->itrace_synth_opts->set) { > + etm->synth_opts.add_callchain = true; > + etm->synth_opts.add_last_branch = true; This causes a segfault in perf report -D because Perf expects the sample to have these things, but we never add them. Maybe we can skip setting these when dump_trace is true? This also leads me to wonder if we should be dumping the trace for each sample. The raw dump is a very well used debug feature, and I'm not sure if aux sampling use cases suggest that it wouldn't be just as useful here too. > + } > + } > + > if (etm->synth_opts.calls) > etm->branches_filter |= PERF_IP_FLAG_CALL | > PERF_IP_FLAG_TRACE_BEGIN | > @@ -3828,11 +4044,13 @@ 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) { > + if ((etm->synth_opts.callchain || etm->synth_opts.add_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->synth_opts.add_callchain = false; > } > } > > @@ -3859,7 +4077,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, > /* Use virtual timestamps if all ETMs report ts_source = 1 */ > etm->has_virtual_ts = cs_etm__has_virtual_ts(metadata, num_cpu); > > - if (!etm->has_virtual_ts) > + if (!etm->has_virtual_ts && !etm->sampling_mode) > ui__warning("Virtual timestamps are not enabled, or not supported by the traced system.\n" > "The time field of the samples will not be set accurately.\n" > "For Arm CPUs prior to Armv8.4 or without support FEAT_TRF,\n" Not sure if there is a need to change this? ETE always has virtual timestamps and sampling mode only supports ETE. > @@ -3875,6 +4093,8 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, > session->auxtrace = &etm->auxtrace; > > cs_etm__setup_timeless_decoding(etm); > + if (etm->sampling_mode) > + etm->timeless_decoding = true; I suppose this is because you are only decoding to attach branch history to samples. If we also supported generating other branch and instruction samples from aux samples then timestamps could be useful. But then again, large aux samples with lots of filtering could still cover large time periods and multiple PIDs. Maybe you would want context IDs or timestamps to be able to decode properly? What would it look like once this is based off of the JIT patches and we just use push_heap() followed by cs_etm__pop_heap() in cs_etm__process_sample(). That should still result in the branch stack being attached to the sample without any other changes, and without worrying about whether timestamps or context IDs are enabled or not. > > etm->tc.time_shift = tc->time_shift; > etm->tc.time_mult = tc->time_mult; > @@ -3889,9 +4109,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.add_last_branch || > + etm->synth_opts.add_callchain || > etm->synth_opts.callchain; > > etm->use_callchain = etm->synth_opts.thread_stack || > + etm->synth_opts.add_callchain || > etm->synth_opts.callchain; > > if (etm->synth_opts.last_branch || etm->synth_opts.add_last_branch) { > @@ -3899,13 +4121,18 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, > etm->br_stack_sz_plus = etm->br_stack_sz; > } > > + err = cs_etm__aux_sample_init(etm); > + if (err) > + goto err_free_queues; > + > if (etm->synth_opts.add_last_branch) { > /* > * Existing samples are matched to decoded trace by time, so > * the trace must carry timestamps that are correlated to perf > * time and the queues must be decoded in time order. > */ > - if (etm->timeless_decoding || !etm->has_virtual_ts) { > + if (!etm->sampling_mode && > + (etm->timeless_decoding || !etm->has_virtual_ts)) { > pr_err("CS ETM Trace: --itrace=L requires virtual timestamped trace\n"); > err = -EINVAL; > goto err_free_queues; > @@ -3920,7 +4147,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, > if (err) > goto err_free_queues; > > - err = cs_etm__queue_aux_records(session); > + err = cs_etm__prepare_auxtrace_queues(etm, session); > if (err) > goto err_free_queues; > > @@ -3972,6 +4199,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, > session->auxtrace = NULL; > err_free_etm: > zfree(&etm->br_stack); > + zfree(&etm->chain); > zfree(&etm); > err_free_metadata: > /* No need to check @metadata[j], free(NULL) is supported */ >