1//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
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#include "llvm/XRay/FDRRecords.h"
9
10namespace llvm {
11namespace xray {
12
13Error RecordInitializer::visit(BufferExtents &R) {
14 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr, length: sizeof(uint64_t)))
15 return createStringError(
16 EC: std::make_error_code(e: std::errc::bad_address),
17 Fmt: "Invalid offset for a buffer extent (%" PRId64 ").", Vals: OffsetPtr);
18
19 auto PreReadOffset = OffsetPtr;
20 R.Size = E.getU64(offset_ptr: &OffsetPtr);
21 if (PreReadOffset == OffsetPtr)
22 return createStringError(EC: std::make_error_code(e: std::errc::invalid_argument),
23 Fmt: "Cannot read buffer extent at offset %" PRId64 ".",
24 Vals: OffsetPtr);
25
26 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
27 return Error::success();
28}
29
30Error RecordInitializer::visit(WallclockRecord &R) {
31 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
32 length: MetadataRecord::kMetadataBodySize))
33 return createStringError(
34 EC: std::make_error_code(e: std::errc::bad_address),
35 Fmt: "Invalid offset for a wallclock record (%" PRId64 ").", Vals: OffsetPtr);
36 auto BeginOffset = OffsetPtr;
37 auto PreReadOffset = OffsetPtr;
38 R.Seconds = E.getU64(offset_ptr: &OffsetPtr);
39 if (OffsetPtr == PreReadOffset)
40 return createStringError(
41 EC: std::make_error_code(e: std::errc::invalid_argument),
42 Fmt: "Cannot read wall clock 'seconds' field at offset %" PRId64 ".",
43 Vals: OffsetPtr);
44
45 PreReadOffset = OffsetPtr;
46 R.Nanos = E.getU32(offset_ptr: &OffsetPtr);
47 if (OffsetPtr == PreReadOffset)
48 return createStringError(
49 EC: std::make_error_code(e: std::errc::invalid_argument),
50 Fmt: "Cannot read wall clock 'nanos' field at offset %" PRId64 ".",
51 Vals: OffsetPtr);
52
53 // Align to metadata record size boundary.
54 assert(OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
55 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
56 return Error::success();
57}
58
59Error RecordInitializer::visit(NewCPUIDRecord &R) {
60 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
61 length: MetadataRecord::kMetadataBodySize))
62 return createStringError(
63 EC: std::make_error_code(e: std::errc::bad_address),
64 Fmt: "Invalid offset for a new cpu id record (%" PRId64 ").", Vals: OffsetPtr);
65 auto BeginOffset = OffsetPtr;
66 auto PreReadOffset = OffsetPtr;
67 R.CPUId = E.getU16(offset_ptr: &OffsetPtr);
68 if (OffsetPtr == PreReadOffset)
69 return createStringError(EC: std::make_error_code(e: std::errc::invalid_argument),
70 Fmt: "Cannot read CPU id at offset %" PRId64 ".",
71 Vals: OffsetPtr);
72
73 PreReadOffset = OffsetPtr;
74 R.TSC = E.getU64(offset_ptr: &OffsetPtr);
75 if (OffsetPtr == PreReadOffset)
76 return createStringError(EC: std::make_error_code(e: std::errc::invalid_argument),
77 Fmt: "Cannot read CPU TSC at offset %" PRId64 ".",
78 Vals: OffsetPtr);
79
80 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
81 return Error::success();
82}
83
84Error RecordInitializer::visit(TSCWrapRecord &R) {
85 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
86 length: MetadataRecord::kMetadataBodySize))
87 return createStringError(
88 EC: std::make_error_code(e: std::errc::bad_address),
89 Fmt: "Invalid offset for a new TSC wrap record (%" PRId64 ").", Vals: OffsetPtr);
90
91 auto PreReadOffset = OffsetPtr;
92 R.BaseTSC = E.getU64(offset_ptr: &OffsetPtr);
93 if (PreReadOffset == OffsetPtr)
94 return createStringError(
95 EC: std::make_error_code(e: std::errc::invalid_argument),
96 Fmt: "Cannot read TSC wrap record at offset %" PRId64 ".", Vals: OffsetPtr);
97
98 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
99 return Error::success();
100}
101
102Error RecordInitializer::visit(CustomEventRecord &R) {
103 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
104 length: MetadataRecord::kMetadataBodySize))
105 return createStringError(
106 EC: std::make_error_code(e: std::errc::bad_address),
107 Fmt: "Invalid offset for a custom event record (%" PRId64 ").", Vals: OffsetPtr);
108
109 auto BeginOffset = OffsetPtr;
110 auto PreReadOffset = OffsetPtr;
111 R.Size = E.getSigned(offset_ptr: &OffsetPtr, size: sizeof(int32_t));
112 if (PreReadOffset == OffsetPtr)
113 return createStringError(
114 EC: std::make_error_code(e: std::errc::invalid_argument),
115 Fmt: "Cannot read a custom event record size field offset %" PRId64 ".",
116 Vals: OffsetPtr);
117
118 if (R.Size <= 0)
119 return createStringError(
120 EC: std::make_error_code(e: std::errc::bad_address),
121 Fmt: "Invalid size for custom event (size = %d) at offset %" PRId64 ".",
122 Vals: R.Size, Vals: OffsetPtr);
123
124 PreReadOffset = OffsetPtr;
125 R.TSC = E.getU64(offset_ptr: &OffsetPtr);
126 if (PreReadOffset == OffsetPtr)
127 return createStringError(
128 EC: std::make_error_code(e: std::errc::invalid_argument),
129 Fmt: "Cannot read a custom event TSC field at offset %" PRId64 ".",
130 Vals: OffsetPtr);
131
132 // For version 4 onwards, of the FDR log, we want to also capture the CPU ID
133 // of the custom event.
134 if (Version >= 4) {
135 PreReadOffset = OffsetPtr;
136 R.CPU = E.getU16(offset_ptr: &OffsetPtr);
137 if (PreReadOffset == OffsetPtr)
138 return createStringError(
139 EC: std::make_error_code(e: std::errc::invalid_argument),
140 Fmt: "Missing CPU field at offset %" PRId64 ".", Vals: OffsetPtr);
141 }
142
143 assert(OffsetPtr > BeginOffset &&
144 OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
145 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
146
147 // Next we read in a fixed chunk of data from the given offset.
148 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr, length: R.Size))
149 return createStringError(
150 EC: std::make_error_code(e: std::errc::bad_address),
151 Fmt: "Cannot read %d bytes of custom event data from offset %" PRId64 ".",
152 Vals: R.Size, Vals: OffsetPtr);
153
154 std::vector<uint8_t> Buffer;
155 Buffer.resize(new_size: R.Size);
156 PreReadOffset = OffsetPtr;
157 if (E.getU8(offset_ptr: &OffsetPtr, dst: Buffer.data(), count: R.Size) != Buffer.data())
158 return createStringError(
159 EC: std::make_error_code(e: std::errc::invalid_argument),
160 Fmt: "Failed reading data into buffer of size %d at offset %" PRId64 ".",
161 Vals: R.Size, Vals: OffsetPtr);
162
163 assert(OffsetPtr >= PreReadOffset);
164 if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
165 return createStringError(
166 EC: std::make_error_code(e: std::errc::invalid_argument),
167 Fmt: "Failed reading enough bytes for the custom event payload -- read "
168 "%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
169 Vals: OffsetPtr - PreReadOffset, Vals: R.Size, Vals: PreReadOffset);
170
171 R.Data.assign(first: Buffer.begin(), last: Buffer.end());
172 return Error::success();
173}
174
175Error RecordInitializer::visit(CustomEventRecordV5 &R) {
176 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
177 length: MetadataRecord::kMetadataBodySize))
178 return createStringError(
179 EC: std::make_error_code(e: std::errc::bad_address),
180 Fmt: "Invalid offset for a custom event record (%" PRId64 ").", Vals: OffsetPtr);
181
182 auto BeginOffset = OffsetPtr;
183 auto PreReadOffset = OffsetPtr;
184
185 R.Size = E.getSigned(offset_ptr: &OffsetPtr, size: sizeof(int32_t));
186 if (PreReadOffset == OffsetPtr)
187 return createStringError(
188 EC: std::make_error_code(e: std::errc::invalid_argument),
189 Fmt: "Cannot read a custom event record size field offset %" PRId64 ".",
190 Vals: OffsetPtr);
191
192 if (R.Size <= 0)
193 return createStringError(
194 EC: std::make_error_code(e: std::errc::bad_address),
195 Fmt: "Invalid size for custom event (size = %d) at offset %" PRId64 ".",
196 Vals: R.Size, Vals: OffsetPtr);
197
198 PreReadOffset = OffsetPtr;
199 R.Delta = E.getSigned(offset_ptr: &OffsetPtr, size: sizeof(int32_t));
200 if (PreReadOffset == OffsetPtr)
201 return createStringError(
202 EC: std::make_error_code(e: std::errc::invalid_argument),
203 Fmt: "Cannot read a custom event record TSC delta field at offset "
204 "%" PRId64 ".",
205 Vals: OffsetPtr);
206
207 assert(OffsetPtr > BeginOffset &&
208 OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
209 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
210
211 // Next we read in a fixed chunk of data from the given offset.
212 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr, length: R.Size))
213 return createStringError(
214 EC: std::make_error_code(e: std::errc::bad_address),
215 Fmt: "Cannot read %d bytes of custom event data from offset %" PRId64 ".",
216 Vals: R.Size, Vals: OffsetPtr);
217
218 std::vector<uint8_t> Buffer;
219 Buffer.resize(new_size: R.Size);
220 PreReadOffset = OffsetPtr;
221 if (E.getU8(offset_ptr: &OffsetPtr, dst: Buffer.data(), count: R.Size) != Buffer.data())
222 return createStringError(
223 EC: std::make_error_code(e: std::errc::invalid_argument),
224 Fmt: "Failed reading data into buffer of size %d at offset %" PRId64 ".",
225 Vals: R.Size, Vals: OffsetPtr);
226
227 assert(OffsetPtr >= PreReadOffset);
228 if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
229 return createStringError(
230 EC: std::make_error_code(e: std::errc::invalid_argument),
231 Fmt: "Failed reading enough bytes for the custom event payload -- read "
232 "%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
233 Vals: OffsetPtr - PreReadOffset, Vals: R.Size, Vals: PreReadOffset);
234
235 R.Data.assign(first: Buffer.begin(), last: Buffer.end());
236 return Error::success();
237}
238
239Error RecordInitializer::visit(TypedEventRecord &R) {
240 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
241 length: MetadataRecord::kMetadataBodySize))
242 return createStringError(
243 EC: std::make_error_code(e: std::errc::bad_address),
244 Fmt: "Invalid offset for a typed event record (%" PRId64 ").", Vals: OffsetPtr);
245
246 auto BeginOffset = OffsetPtr;
247 auto PreReadOffset = OffsetPtr;
248
249 R.Size = E.getSigned(offset_ptr: &OffsetPtr, size: sizeof(int32_t));
250 if (PreReadOffset == OffsetPtr)
251 return createStringError(
252 EC: std::make_error_code(e: std::errc::invalid_argument),
253 Fmt: "Cannot read a typed event record size field offset %" PRId64 ".",
254 Vals: OffsetPtr);
255
256 if (R.Size <= 0)
257 return createStringError(
258 EC: std::make_error_code(e: std::errc::bad_address),
259 Fmt: "Invalid size for typed event (size = %d) at offset %" PRId64 ".",
260 Vals: R.Size, Vals: OffsetPtr);
261
262 PreReadOffset = OffsetPtr;
263 R.Delta = E.getSigned(offset_ptr: &OffsetPtr, size: sizeof(int32_t));
264 if (PreReadOffset == OffsetPtr)
265 return createStringError(
266 EC: std::make_error_code(e: std::errc::invalid_argument),
267 Fmt: "Cannot read a typed event record TSC delta field at offset "
268 "%" PRId64 ".",
269 Vals: OffsetPtr);
270
271 PreReadOffset = OffsetPtr;
272 R.EventType = E.getU16(offset_ptr: &OffsetPtr);
273 if (PreReadOffset == OffsetPtr)
274 return createStringError(
275 EC: std::make_error_code(e: std::errc::invalid_argument),
276 Fmt: "Cannot read a typed event record type field at offset %" PRId64 ".",
277 Vals: OffsetPtr);
278
279 assert(OffsetPtr > BeginOffset &&
280 OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
281 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
282
283 // Next we read in a fixed chunk of data from the given offset.
284 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr, length: R.Size))
285 return createStringError(
286 EC: std::make_error_code(e: std::errc::bad_address),
287 Fmt: "Cannot read %d bytes of custom event data from offset %" PRId64 ".",
288 Vals: R.Size, Vals: OffsetPtr);
289
290 std::vector<uint8_t> Buffer;
291 Buffer.resize(new_size: R.Size);
292 PreReadOffset = OffsetPtr;
293 if (E.getU8(offset_ptr: &OffsetPtr, dst: Buffer.data(), count: R.Size) != Buffer.data())
294 return createStringError(
295 EC: std::make_error_code(e: std::errc::invalid_argument),
296 Fmt: "Failed reading data into buffer of size %d at offset %" PRId64 ".",
297 Vals: R.Size, Vals: OffsetPtr);
298
299 assert(OffsetPtr >= PreReadOffset);
300 if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
301 return createStringError(
302 EC: std::make_error_code(e: std::errc::invalid_argument),
303 Fmt: "Failed reading enough bytes for the typed event payload -- read "
304 "%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
305 Vals: OffsetPtr - PreReadOffset, Vals: R.Size, Vals: PreReadOffset);
306
307 R.Data.assign(first: Buffer.begin(), last: Buffer.end());
308 return Error::success();
309}
310
311Error RecordInitializer::visit(CallArgRecord &R) {
312 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
313 length: MetadataRecord::kMetadataBodySize))
314 return createStringError(
315 EC: std::make_error_code(e: std::errc::bad_address),
316 Fmt: "Invalid offset for a call argument record (%" PRId64 ").",
317 Vals: OffsetPtr);
318
319 auto PreReadOffset = OffsetPtr;
320 R.Arg = E.getU64(offset_ptr: &OffsetPtr);
321 if (PreReadOffset == OffsetPtr)
322 return createStringError(
323 EC: std::make_error_code(e: std::errc::invalid_argument),
324 Fmt: "Cannot read a call arg record at offset %" PRId64 ".", Vals: OffsetPtr);
325
326 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
327 return Error::success();
328}
329
330Error RecordInitializer::visit(PIDRecord &R) {
331 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
332 length: MetadataRecord::kMetadataBodySize))
333 return createStringError(
334 EC: std::make_error_code(e: std::errc::bad_address),
335 Fmt: "Invalid offset for a process ID record (%" PRId64 ").", Vals: OffsetPtr);
336
337 auto PreReadOffset = OffsetPtr;
338 R.PID = E.getSigned(offset_ptr: &OffsetPtr, size: 4);
339 if (PreReadOffset == OffsetPtr)
340 return createStringError(
341 EC: std::make_error_code(e: std::errc::invalid_argument),
342 Fmt: "Cannot read a process ID record at offset %" PRId64 ".", Vals: OffsetPtr);
343
344 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
345 return Error::success();
346}
347
348Error RecordInitializer::visit(NewBufferRecord &R) {
349 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
350 length: MetadataRecord::kMetadataBodySize))
351 return createStringError(
352 EC: std::make_error_code(e: std::errc::bad_address),
353 Fmt: "Invalid offset for a new buffer record (%" PRId64 ").", Vals: OffsetPtr);
354
355 auto PreReadOffset = OffsetPtr;
356 R.TID = E.getSigned(offset_ptr: &OffsetPtr, size: sizeof(int32_t));
357 if (PreReadOffset == OffsetPtr)
358 return createStringError(
359 EC: std::make_error_code(e: std::errc::invalid_argument),
360 Fmt: "Cannot read a new buffer record at offset %" PRId64 ".", Vals: OffsetPtr);
361
362 OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
363 return Error::success();
364}
365
366Error RecordInitializer::visit(EndBufferRecord &R) {
367 if (!E.isValidOffsetForDataOfSize(offset: OffsetPtr,
368 length: MetadataRecord::kMetadataBodySize))
369 return createStringError(
370 EC: std::make_error_code(e: std::errc::bad_address),
371 Fmt: "Invalid offset for an end-of-buffer record (%" PRId64 ").",
372 Vals: OffsetPtr);
373
374 OffsetPtr += MetadataRecord::kMetadataBodySize;
375 return Error::success();
376}
377
378Error RecordInitializer::visit(FunctionRecord &R) {
379 // For function records, we need to retreat one byte back to read a full
380 // unsigned 32-bit value. The first four bytes will have the following
381 // layout:
382 //
383 // bit 0 : function record indicator (must be 0)
384 // bits 1..3 : function record type
385 // bits 4..32 : function id
386 //
387 if (OffsetPtr == 0 || !E.isValidOffsetForDataOfSize(
388 offset: --OffsetPtr, length: FunctionRecord::kFunctionRecordSize))
389 return createStringError(
390 EC: std::make_error_code(e: std::errc::bad_address),
391 Fmt: "Invalid offset for a function record (%" PRId64 ").", Vals: OffsetPtr);
392
393 auto BeginOffset = OffsetPtr;
394 auto PreReadOffset = BeginOffset;
395 uint32_t Buffer = E.getU32(offset_ptr: &OffsetPtr);
396 if (PreReadOffset == OffsetPtr)
397 return createStringError(
398 EC: std::make_error_code(e: std::errc::bad_address),
399 Fmt: "Cannot read function id field from offset %" PRId64 ".", Vals: OffsetPtr);
400
401 // To get the function record type, we shift the buffer one to the right
402 // (truncating the function record indicator) then take the three bits
403 // (0b0111) to get the record type as an unsigned value.
404 unsigned FunctionType = (Buffer >> 1) & 0x07u;
405 switch (FunctionType) {
406 case static_cast<unsigned>(RecordTypes::ENTER):
407 case static_cast<unsigned>(RecordTypes::ENTER_ARG):
408 case static_cast<unsigned>(RecordTypes::EXIT):
409 case static_cast<unsigned>(RecordTypes::TAIL_EXIT):
410 R.Kind = static_cast<RecordTypes>(FunctionType);
411 break;
412 default:
413 return createStringError(
414 EC: std::make_error_code(e: std::errc::invalid_argument),
415 Fmt: "Unknown function record type '%d' at offset %" PRId64 ".",
416 Vals: FunctionType, Vals: BeginOffset);
417 }
418
419 R.FuncId = Buffer >> 4;
420 PreReadOffset = OffsetPtr;
421 R.Delta = E.getU32(offset_ptr: &OffsetPtr);
422 if (OffsetPtr == PreReadOffset)
423 return createStringError(
424 EC: std::make_error_code(e: std::errc::invalid_argument),
425 Fmt: "Failed reading TSC delta from offset %" PRId64 ".", Vals: OffsetPtr);
426 assert(FunctionRecord::kFunctionRecordSize == (OffsetPtr - BeginOffset));
427 return Error::success();
428}
429
430} // namespace xray
431} // namespace llvm
432