[llvm] [llvm-profgen] Add support for ETM trace decoding (PR #191584)
Peter Smith via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 14 13:55:14 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()) {
----------------
smithp35 wrote:
I think this would need to be derived from the Build Attributes.
I think you may need a manual override for the triple for when Build Attributes aren't present or aren't correct.
Build Attributes are only defined for relocatable objects, GNU ld and lld do leave them in an executable, but this is not guaranteed by the specification. LLD's implementation of 32-bit build attributes is also rudimentary, it just propagates the first object's rather than merging them.
https://github.com/llvm/llvm-project/pull/191584
More information about the llvm-commits
mailing list