[llvm] [llvm-profgen] Add support for ETM trace decoding (PR #191584)
Peter Smith via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 16 11:54:15 PDT 2026
================
@@ -0,0 +1,223 @@
+//===-- ETMTraceDecoder.cpp - ETM Trace Decoder -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/ETMTraceDecoder.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/TargetParser/ARMTargetParser.h"
+
+#ifdef HAVE_OPENCSD
+#include "opencsd/c_api/opencsd_c_api.h"
+
+namespace llvm {
+
+namespace {
+class ETMDecoderImpl : public ETMDecoder {
+ dcd_tree_handle_t DcdTree = 0;
+ const object::Binary &Binary;
+ const Triple &TargetTriple;
+
+ // Trace processing and Callback handling.
+ static ocsd_datapath_resp_t
+ processTrace(const void *PContext, const ocsd_trc_index_t /*IndexSOP*/,
+ const uint8_t /*TrcChanID*/,
+ const ocsd_generic_trace_elem *Element) {
+ auto *Decoder = static_cast<ETMDecoderImpl *>(const_cast<void *>(PContext));
+ if (!Decoder || !Element)
+ return OCSD_RESP_FATAL_SYS_ERR;
+
+ // Process instruction ranges reconstructed by the decoder.
+ if (Element->elem_type == OCSD_GEN_TRC_ELEM_INSTR_RANGE) {
+ uint64_t Start = Element->st_addr;
+ uint64_t End = Element->en_addr;
+ if (End > Start) {
+ // OpenCSD ranges are exclusive at the end [Start, End).
+ // llvm-profgen range counters expect inclusive bounds [Start, End].
+ // Adjust the exclusive end address provided by OpenCSD to include
+ // the last executed instruction within the reported range.
+ Decoder->CurrentCallback->processInstructionRange(Start, End - 1);
+ }
+ }
+ return OCSD_RESP_CONT;
+ }
+
+ Callback *CurrentCallback = nullptr;
+
+ // Iterate through the ELF program headers to collect all executable LOAD
+ // segments. These are registered as a single transaction to the OpenCSD
+ // memory manager to prevent overlap/collision errors between different
+ // memory regions.
+ Error mapELFSegments(dcd_tree_handle_t DcdTree,
+ const object::Binary &SourceBin) {
+ SmallVector<ocsd_file_mem_region_t, 4> Regions;
+ auto ProcessHeaders = [&](const auto &ElfFile) {
+ auto ProgramHeaders = ElfFile.program_headers();
+ if (!ProgramHeaders)
+ return;
+
+ for (const auto &Phdr : *ProgramHeaders) {
+ if (Phdr.p_type == llvm::ELF::PT_LOAD &&
+ (Phdr.p_flags & llvm::ELF::PF_X)) {
+ ocsd_file_mem_region_t Region{};
+ Region.start_address = (uint64_t)Phdr.p_vaddr;
+ Region.file_offset = (uint64_t)Phdr.p_offset;
+ Region.region_size = (uint64_t)Phdr.p_filesz;
+ Regions.push_back(Region);
+ }
+ }
+ };
+
+ if (auto *O = dyn_cast<object::ELF32LEObjectFile>(&SourceBin))
+ ProcessHeaders(O->getELFFile());
+ else if (auto *O = dyn_cast<object::ELF64LEObjectFile>(&SourceBin))
+ ProcessHeaders(O->getELFFile());
+ else if (auto *O = dyn_cast<object::ELF32BEObjectFile>(&SourceBin))
+ ProcessHeaders(O->getELFFile());
+ else if (auto *O = dyn_cast<object::ELF64BEObjectFile>(&SourceBin))
+ ProcessHeaders(O->getELFFile());
+
+ if (!Regions.empty()) {
+ std::string Path = SourceBin.getFileName().str();
+ if (ocsd_dt_add_binfile_region_mem_acc(
+ DcdTree, Regions.data(), (uint32_t)Regions.size(),
+ OCSD_MEM_SPACE_ANY, Path.c_str()) != 0) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "OpenCSD: Failed to map ELF executable segments.");
+ }
+ }
+ return Error::success();
+ }
+
+public:
+ ETMDecoderImpl(const object::Binary &Binary, const Triple &Triple)
+ : Binary(Binary), TargetTriple(Triple) {}
+
+ ~ETMDecoderImpl() override {
+ if (DcdTree)
+ // Deallocate the decoder tree resources.
+ ocsd_destroy_dcd_tree(DcdTree);
+ }
+
+ // Initialize the decoder by auto-detecting the target architecture and
+ // configuring the OpenCSD decoder.
+ Error initialize() {
+ DcdTree = ocsd_create_dcd_tree(OCSD_TRC_SRC_SINGLE, 0);
+ if (!DcdTree)
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to create OpenCSD decoder tree.");
+
+ // Configure and initialize the instruction-level decoder.
+ ocsd_arch_version_t ArchVer = ARCH_UNKNOWN;
+ if (TargetTriple.isArmMClass()) {
+ if (ARM::parseArchVersion(TargetTriple.getArchName()) >= 8)
+ ArchVer = ARCH_V8;
+ else
+ ArchVer = ARCH_V7;
+ }
----------------
smithp35 wrote:
The Cortex-M0 and Cortex-M0+ which are Arm v6-m do not have ETM support, they have some basic tracing support called MTB which isn't supported by OpenCSD.
I suggest looking for a triple getArchName() of 6 and using `ARCH_UNKNOWN`. Then in the error message you could alter the message text to:
```
Only Arm M-profile (Cortex-M) with ETM support is currently supported.
```
Apologies for the Arm pedantry, using profile instead of class.
MTB reference:
https://developer.arm.com/documentation/ddi0486/b/introduction/about-the-coresight-mtb-m0-
https://github.com/llvm/llvm-project/pull/191584
More information about the llvm-commits
mailing list