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 | |
10 | namespace llvm { |
11 | namespace xray { |
12 | |
13 | Error 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 | |
30 | Error 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 | |
59 | Error 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 | |
84 | Error 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 | |
102 | Error 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 | |
175 | Error 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 | |
239 | Error 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 | |
311 | Error 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 | |
330 | Error 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 | |
348 | Error 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 | |
366 | Error 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 | |
378 | Error 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 | |