[llvm] [llvm-profgen] Add support for ETM trace decoding (PR #191584)
Amir Ayupov via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 21:59:53 PDT 2026
================
@@ -0,0 +1,220 @@
+//===-- 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"
+
+#ifdef HAVE_OPENCSD
+#include "opencsd/c_api/opencsd_c_api.h"
+
+namespace llvm {
+
+namespace {
+class ETMDecoderImpl : public ETMDecoder {
+ dcd_tree_handle_t DcdTree = 0;
+ std::string BinaryPath;
+ 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, 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()) {
+ if (ocsd_dt_add_binfile_region_mem_acc(
+ DcdTree, Regions.data(), (uint32_t)Regions.size(),
+ OCSD_MEM_SPACE_ANY, BinaryPath.c_str()) != 0) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "OpenCSD: Failed to map ELF executable segments.");
+ }
+ }
+ return Error::success();
+ }
+
+public:
+ ETMDecoderImpl(StringRef BinaryPath) : BinaryPath(BinaryPath.str()) {}
+ ~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() {
+ auto BinaryOrErr = object::createBinary(BinaryPath);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+
+ object::Binary &SourceBin = *BinaryOrErr.get().getBinary();
+ auto *ElfBase = dyn_cast<object::ELFObjectFileBase>(&SourceBin);
+ if (!ElfBase)
+ return createStringError(inconvertibleErrorCode(),
+ "OpenCSD: Unsupported binary format. Only ELF "
+ "is currently supported.");
+
+ TargetTriple = ElfBase->makeTriple();
+
+ 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_etmv4_cfg Config{};
+ // Initialize the decoder for microcontroller-class targets.
+ if (TargetTriple.isArmMClass() || TargetTriple.isThumb()) {
+ Config.arch_ver = ARCH_V8;
+ Config.core_prof = profile_CortexM;
+ } else {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "OpenCSD: Unsupported processor architecture. Only "
+ "microcontroller-class (Cortex-M) is currently supported.");
+ }
----------------
aaupov wrote:
Is supporting `CortexA` only a matter of setting the `Config.core_prof` accordingly? Can you also add it too?
https://github.com/llvm/llvm-project/pull/191584
More information about the llvm-commits
mailing list