LLVM 23.0.0git
ETMTraceDecoder.cpp
Go to the documentation of this file.
1//===-- ETMTraceDecoder.cpp - ETM Trace Decoder -----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
13#include "llvm/Support/Error.h"
15
16#ifdef HAVE_OPENCSD
17#include "opencsd/c_api/opencsd_c_api.h"
18
19namespace llvm {
20
21namespace {
22
23class HardwareTraceConfig {
24public:
25 virtual ~HardwareTraceConfig() = default;
26};
27
28class ETMTraceConfig : public HardwareTraceConfig {
29public:
30 ocsd_etmv4_cfg Cfg{};
31 uint8_t TraceID;
32
33 ETMTraceConfig(const Triple &TargetTriple, uint8_t TraceID)
34 : TraceID(TraceID) {
35 ocsd_arch_version_t ArchVer = ARCH_UNKNOWN;
36 if (TargetTriple.isArmMClass()) {
37 unsigned ArchVersion = ARM::parseArchVersion(TargetTriple.getArchName());
38 if (ArchVersion >= 8)
39 ArchVer = ARCH_V8;
40 else if (ArchVersion == 7)
41 ArchVer = ARCH_V7;
42 else
43 // For version 6 (Cortex-M0) and others.
44 ArchVer = ARCH_UNKNOWN;
45 }
46 // Initialize the decoder for Arm M-profile targets.
47 Cfg.arch_ver = ArchVer;
48 Cfg.core_prof = profile_CortexM;
49
50 // The CoreSight Trace ID (CSID) is a hardware-assigned 7-bit identifier
51 // used to route trace data.
52 Cfg.reg_traceidr = TraceID;
53 }
54
55 Error validate() const {
56 if (Cfg.arch_ver == ARCH_UNKNOWN)
57 return createStringError(
59 "OpenCSD: Unsupported processor architecture. Only Arm M-profile "
60 "(Cortex-M) with ETM support is currently supported.");
61 return Error::success();
62 }
63};
64
65class ETMDecoderImpl : public ETMDecoder {
66 dcd_tree_handle_t DcdTree = 0;
67 const object::Binary &Binary;
68 const Triple &TargetTriple;
69
70 // Trace processing and Callback handling.
71 static ocsd_datapath_resp_t
72 processTrace(const void *PContext, const ocsd_trc_index_t /*IndexSOP*/,
73 const uint8_t /*TrcChanID*/,
74 const ocsd_generic_trace_elem *Element) {
75 auto *Decoder = static_cast<ETMDecoderImpl *>(const_cast<void *>(PContext));
76 if (!Decoder || !Element)
77 return OCSD_RESP_FATAL_SYS_ERR;
78
79 // Process instruction ranges reconstructed by the decoder.
80 if (Element->elem_type == OCSD_GEN_TRC_ELEM_INSTR_RANGE) {
81 uint64_t Start = Element->st_addr;
82 uint64_t End = Element->en_addr;
83 if (End > Start) {
84 // OpenCSD ranges are exclusive at the end [Start, End).
85 // llvm-profgen range counters expect inclusive bounds [Start, End].
86 // Adjust the exclusive end address provided by OpenCSD to include
87 // the last executed instruction within the reported range.
88 Decoder->CurrentCallback->processInstructionRange(Start, End - 1);
89 }
90 }
91 return OCSD_RESP_CONT;
92 }
93
94 Callback *CurrentCallback = nullptr;
95
96 // Iterate through the ELF program headers to collect all executable LOAD
97 // segments. These are registered as a single transaction to the OpenCSD
98 // memory manager to prevent overlap/collision errors between different
99 // memory regions.
100 Error mapELFSegments(dcd_tree_handle_t DcdTree,
101 const object::Binary &SourceBin) {
102 SmallVector<ocsd_file_mem_region_t, 4> Regions;
103 auto ProcessHeaders = [&](const auto &ElfFile) {
104 auto ProgramHeaders = ElfFile.program_headers();
105 if (!ProgramHeaders)
106 return;
107
108 for (const auto &Phdr : *ProgramHeaders) {
109 if (Phdr.p_type == llvm::ELF::PT_LOAD &&
110 (Phdr.p_flags & llvm::ELF::PF_X)) {
111 ocsd_file_mem_region_t Region{};
112 Region.start_address = (uint64_t)Phdr.p_vaddr;
113 Region.file_offset = (uint64_t)Phdr.p_offset;
114 Region.region_size = (uint64_t)Phdr.p_filesz;
115 Regions.push_back(Region);
116 }
117 }
118 };
119
120 if (auto *O = dyn_cast<object::ELF32LEObjectFile>(&SourceBin))
121 ProcessHeaders(O->getELFFile());
122 else if (auto *O = dyn_cast<object::ELF64LEObjectFile>(&SourceBin))
123 ProcessHeaders(O->getELFFile());
124 else if (auto *O = dyn_cast<object::ELF32BEObjectFile>(&SourceBin))
125 ProcessHeaders(O->getELFFile());
126 else if (auto *O = dyn_cast<object::ELF64BEObjectFile>(&SourceBin))
127 ProcessHeaders(O->getELFFile());
128
129 if (!Regions.empty()) {
130 std::string Path = SourceBin.getFileName().str();
131 if (ocsd_dt_add_binfile_region_mem_acc(
132 DcdTree, Regions.data(), (uint32_t)Regions.size(),
133 OCSD_MEM_SPACE_ANY, Path.c_str()) != 0) {
134 return createStringError(
136 "OpenCSD: Failed to map ELF executable segments.");
137 }
138 }
139 return Error::success();
140 }
141
142public:
143 uint8_t TraceID;
144
145 ETMDecoderImpl(const object::Binary &Binary, const Triple &Triple,
146 uint8_t TraceID)
147 : Binary(Binary), TargetTriple(Triple), TraceID(TraceID) {}
148
149 ~ETMDecoderImpl() override {
150 if (DcdTree)
151 // Deallocate the decoder tree resources.
152 ocsd_destroy_dcd_tree(DcdTree);
153 }
154
155 // Initialize the decoder by auto-detecting the target architecture and
156 // configuring the OpenCSD decoder.
157 Error initialize() {
158 DcdTree = ocsd_create_dcd_tree(OCSD_TRC_SRC_SINGLE, 0);
159 if (!DcdTree)
161 "Failed to create OpenCSD decoder tree.");
162
163 // Configure and initialize the instruction-level decoder.
164 ETMTraceConfig Config(TargetTriple, TraceID);
165 if (Error E = Config.validate())
166 return E;
167
168 uint32_t Flags =
169 OCSD_CREATE_FLG_FULL_DECODER | OCSD_OPFLG_CHK_RANGE_CONTINUE;
170 if (ocsd_dt_create_decoder(DcdTree, OCSD_BUILTIN_DCD_ETMV4I, Flags,
171 (void *)&Config.Cfg, &Config.TraceID) != 0)
172 return createStringError(
174 "OpenCSD: Failed to initialize the instruction decoder.");
175
176 // Extract and map executable segments from the ELF binary.
177 if (Error E = mapELFSegments(DcdTree, Binary))
178 return E;
179
180 // Register the high-level packet callback. The 'processTrace' function
181 // will be invoked for every decoded instruction range.
182 ocsd_dt_set_gen_elem_outfn(DcdTree, processTrace, this);
183 return Error::success();
184 }
185
186 Error processTrace(ArrayRef<uint8_t> TraceData,
187 Callback &TraceCallback) override {
188 CurrentCallback = &TraceCallback;
189 // Initial reset to prime the decoder.
190 ocsd_dt_process_data(DcdTree, OCSD_OP_RESET, 0, 0, nullptr, nullptr);
191
192 const uint8_t *DataPtr = TraceData.data();
193 uint32_t TotalSize = TraceData.size();
194 uint32_t Processed = 0;
195
196 // Core Decoding Loop.
197 while (Processed < TotalSize) {
198 uint32_t Consumed = 0;
199 uint32_t Remaining = TotalSize - Processed;
200 ocsd_datapath_resp_t Response =
201 ocsd_dt_process_data(DcdTree, OCSD_OP_DATA, Processed, Remaining,
202 DataPtr + Processed, &Consumed);
203
204 if (Response == OCSD_RESP_WAIT) {
205 // Decoder buffers are full; flush to drain internal states.
206 ocsd_dt_process_data(DcdTree, OCSD_OP_FLUSH, 0, 0, nullptr, nullptr);
207 } else if (Consumed == 0 && Processed < TotalSize) {
208 // Decoder stalled; skip byte and reset to find next sync point.
209 Processed++;
210 ocsd_dt_process_data(DcdTree, OCSD_OP_RESET, 0, 0, nullptr, nullptr);
211 } else {
212 // Successfully consumed bytes of the bitstream.
213 Processed += Consumed;
214 }
215
216 if (Response >= OCSD_RESP_FATAL_INVALID_DATA)
218 "OpenCSD: Fatal decoding error.");
219 }
220
221 // Finalize the decoding session by flushing the EOT (End of Trace) marker.
222 ocsd_dt_process_data(DcdTree, OCSD_OP_EOT, 0, 0, nullptr, nullptr);
223 return Error::success();
224 }
225};
226} // namespace
227
229ETMDecoder::create(const object::Binary &Binary, const Triple &Triple,
230 uint8_t TraceID) {
231 auto Decoder = std::make_unique<ETMDecoderImpl>(Binary, Triple, TraceID);
232 if (Error E = Decoder->initialize())
233 return std::move(E);
234 return std::unique_ptr<ETMDecoder>(std::move(Decoder));
235}
236
237} // namespace llvm
238
239#else // !HAVE_OPENCSD
240
241namespace llvm {
242
244ETMDecoder::create(const object::Binary & /*Binary*/, const Triple & /*Triple*/,
245 uint8_t /*TraceID*/) {
246 return createStringError(inconvertibleErrorCode(), "OpenCSD not enabled.");
247}
248
249} // namespace llvm
250
251#endif // HAVE_OPENCSD
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the SmallVector class.
static void initialize(TargetLibraryInfoImpl &TLI, const Triple &T, const llvm::StringTable &StandardNames, VectorLibrary VecLib)
Initialize the set of available library functions based on the specified target triple.
static Expected< std::unique_ptr< ETMDecoder > > create(const object::Binary &Binary, const Triple &TargetTriple, uint8_t TraceID=0x10)
Tagged union holding either a T or a Error.
Definition Error.h:485
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:47
@ PF_X
Definition ELF.h:1609
@ PT_LOAD
Definition ELF.h:1559
void validate(const Triple &TT, const FeatureBitset &FeatureBits)
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Transport.h:139
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321