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 
visit(BufferExtents & R)13 Error RecordInitializer::visit(BufferExtents &R) {
14   if (!E.isValidOffsetForDataOfSize(OffsetPtr, sizeof(uint64_t)))
15     return createStringError(
16         std::make_error_code(std::errc::bad_address),
17         "Invalid offset for a buffer extent (%" PRId64 ").", OffsetPtr);
18 
19   auto PreReadOffset = OffsetPtr;
20   R.Size = E.getU64(&OffsetPtr);
21   if (PreReadOffset == OffsetPtr)
22     return createStringError(std::make_error_code(std::errc::invalid_argument),
23                              "Cannot read buffer extent at offset %" PRId64 ".",
24                              OffsetPtr);
25 
26   OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
27   return Error::success();
28 }
29 
visit(WallclockRecord & R)30 Error RecordInitializer::visit(WallclockRecord &R) {
31   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
32                                     MetadataRecord::kMetadataBodySize))
33     return createStringError(
34         std::make_error_code(std::errc::bad_address),
35         "Invalid offset for a wallclock record (%" PRId64 ").", OffsetPtr);
36   auto BeginOffset = OffsetPtr;
37   auto PreReadOffset = OffsetPtr;
38   R.Seconds = E.getU64(&OffsetPtr);
39   if (OffsetPtr == PreReadOffset)
40     return createStringError(
41         std::make_error_code(std::errc::invalid_argument),
42         "Cannot read wall clock 'seconds' field at offset %" PRId64 ".",
43         OffsetPtr);
44 
45   PreReadOffset = OffsetPtr;
46   R.Nanos = E.getU32(&OffsetPtr);
47   if (OffsetPtr == PreReadOffset)
48     return createStringError(
49         std::make_error_code(std::errc::invalid_argument),
50         "Cannot read wall clock 'nanos' field at offset %" PRId64 ".",
51         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 
visit(NewCPUIDRecord & R)59 Error RecordInitializer::visit(NewCPUIDRecord &R) {
60   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
61                                     MetadataRecord::kMetadataBodySize))
62     return createStringError(
63         std::make_error_code(std::errc::bad_address),
64         "Invalid offset for a new cpu id record (%" PRId64 ").", OffsetPtr);
65   auto BeginOffset = OffsetPtr;
66   auto PreReadOffset = OffsetPtr;
67   R.CPUId = E.getU16(&OffsetPtr);
68   if (OffsetPtr == PreReadOffset)
69     return createStringError(std::make_error_code(std::errc::invalid_argument),
70                              "Cannot read CPU id at offset %" PRId64 ".",
71                              OffsetPtr);
72 
73   PreReadOffset = OffsetPtr;
74   R.TSC = E.getU64(&OffsetPtr);
75   if (OffsetPtr == PreReadOffset)
76     return createStringError(std::make_error_code(std::errc::invalid_argument),
77                              "Cannot read CPU TSC at offset %" PRId64 ".",
78                              OffsetPtr);
79 
80   OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
81   return Error::success();
82 }
83 
visit(TSCWrapRecord & R)84 Error RecordInitializer::visit(TSCWrapRecord &R) {
85   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
86                                     MetadataRecord::kMetadataBodySize))
87     return createStringError(
88         std::make_error_code(std::errc::bad_address),
89         "Invalid offset for a new TSC wrap record (%" PRId64 ").", OffsetPtr);
90 
91   auto PreReadOffset = OffsetPtr;
92   R.BaseTSC = E.getU64(&OffsetPtr);
93   if (PreReadOffset == OffsetPtr)
94     return createStringError(
95         std::make_error_code(std::errc::invalid_argument),
96         "Cannot read TSC wrap record at offset %" PRId64 ".", OffsetPtr);
97 
98   OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
99   return Error::success();
100 }
101 
visit(CustomEventRecord & R)102 Error RecordInitializer::visit(CustomEventRecord &R) {
103   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
104                                     MetadataRecord::kMetadataBodySize))
105     return createStringError(
106         std::make_error_code(std::errc::bad_address),
107         "Invalid offset for a custom event record (%" PRId64 ").", OffsetPtr);
108 
109   auto BeginOffset = OffsetPtr;
110   auto PreReadOffset = OffsetPtr;
111   R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
112   if (PreReadOffset == OffsetPtr)
113     return createStringError(
114         std::make_error_code(std::errc::invalid_argument),
115         "Cannot read a custom event record size field offset %" PRId64 ".",
116         OffsetPtr);
117 
118   if (R.Size <= 0)
119     return createStringError(
120         std::make_error_code(std::errc::bad_address),
121         "Invalid size for custom event (size = %d) at offset %" PRId64 ".",
122         R.Size, OffsetPtr);
123 
124   PreReadOffset = OffsetPtr;
125   R.TSC = E.getU64(&OffsetPtr);
126   if (PreReadOffset == OffsetPtr)
127     return createStringError(
128         std::make_error_code(std::errc::invalid_argument),
129         "Cannot read a custom event TSC field at offset %" PRId64 ".",
130         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(&OffsetPtr);
137     if (PreReadOffset == OffsetPtr)
138       return createStringError(
139           std::make_error_code(std::errc::invalid_argument),
140           "Missing CPU field at offset %" PRId64 ".", 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(OffsetPtr, R.Size))
149     return createStringError(
150         std::make_error_code(std::errc::bad_address),
151         "Cannot read %d bytes of custom event data from offset %" PRId64 ".",
152         R.Size, OffsetPtr);
153 
154   std::vector<uint8_t> Buffer;
155   Buffer.resize(R.Size);
156   PreReadOffset = OffsetPtr;
157   if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
158     return createStringError(
159         std::make_error_code(std::errc::invalid_argument),
160         "Failed reading data into buffer of size %d at offset %" PRId64 ".",
161         R.Size, OffsetPtr);
162 
163   assert(OffsetPtr >= PreReadOffset);
164   if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
165     return createStringError(
166         std::make_error_code(std::errc::invalid_argument),
167         "Failed reading enough bytes for the custom event payload -- read "
168         "%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
169         OffsetPtr - PreReadOffset, R.Size, PreReadOffset);
170 
171   R.Data.assign(Buffer.begin(), Buffer.end());
172   return Error::success();
173 }
174 
visit(CustomEventRecordV5 & R)175 Error RecordInitializer::visit(CustomEventRecordV5 &R) {
176   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
177                                     MetadataRecord::kMetadataBodySize))
178     return createStringError(
179         std::make_error_code(std::errc::bad_address),
180         "Invalid offset for a custom event record (%" PRId64 ").", OffsetPtr);
181 
182   auto BeginOffset = OffsetPtr;
183   auto PreReadOffset = OffsetPtr;
184 
185   R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
186   if (PreReadOffset == OffsetPtr)
187     return createStringError(
188         std::make_error_code(std::errc::invalid_argument),
189         "Cannot read a custom event record size field offset %" PRId64 ".",
190         OffsetPtr);
191 
192   if (R.Size <= 0)
193     return createStringError(
194         std::make_error_code(std::errc::bad_address),
195         "Invalid size for custom event (size = %d) at offset %" PRId64 ".",
196         R.Size, OffsetPtr);
197 
198   PreReadOffset = OffsetPtr;
199   R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t));
200   if (PreReadOffset == OffsetPtr)
201     return createStringError(
202         std::make_error_code(std::errc::invalid_argument),
203         "Cannot read a custom event record TSC delta field at offset "
204         "%" PRId64 ".",
205         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(OffsetPtr, R.Size))
213     return createStringError(
214         std::make_error_code(std::errc::bad_address),
215         "Cannot read %d bytes of custom event data from offset %" PRId64 ".",
216         R.Size, OffsetPtr);
217 
218   std::vector<uint8_t> Buffer;
219   Buffer.resize(R.Size);
220   PreReadOffset = OffsetPtr;
221   if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
222     return createStringError(
223         std::make_error_code(std::errc::invalid_argument),
224         "Failed reading data into buffer of size %d at offset %" PRId64 ".",
225         R.Size, OffsetPtr);
226 
227   assert(OffsetPtr >= PreReadOffset);
228   if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
229     return createStringError(
230         std::make_error_code(std::errc::invalid_argument),
231         "Failed reading enough bytes for the custom event payload -- read "
232         "%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
233         OffsetPtr - PreReadOffset, R.Size, PreReadOffset);
234 
235   R.Data.assign(Buffer.begin(), Buffer.end());
236   return Error::success();
237 }
238 
visit(TypedEventRecord & R)239 Error RecordInitializer::visit(TypedEventRecord &R) {
240   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
241                                     MetadataRecord::kMetadataBodySize))
242     return createStringError(
243         std::make_error_code(std::errc::bad_address),
244         "Invalid offset for a typed event record (%" PRId64 ").", OffsetPtr);
245 
246   auto BeginOffset = OffsetPtr;
247   auto PreReadOffset = OffsetPtr;
248 
249   R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
250   if (PreReadOffset == OffsetPtr)
251     return createStringError(
252         std::make_error_code(std::errc::invalid_argument),
253         "Cannot read a typed event record size field offset %" PRId64 ".",
254         OffsetPtr);
255 
256   if (R.Size <= 0)
257     return createStringError(
258         std::make_error_code(std::errc::bad_address),
259         "Invalid size for typed event (size = %d) at offset %" PRId64 ".",
260         R.Size, OffsetPtr);
261 
262   PreReadOffset = OffsetPtr;
263   R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t));
264   if (PreReadOffset == OffsetPtr)
265     return createStringError(
266         std::make_error_code(std::errc::invalid_argument),
267         "Cannot read a typed event record TSC delta field at offset "
268         "%" PRId64 ".",
269         OffsetPtr);
270 
271   PreReadOffset = OffsetPtr;
272   R.EventType = E.getU16(&OffsetPtr);
273   if (PreReadOffset == OffsetPtr)
274     return createStringError(
275         std::make_error_code(std::errc::invalid_argument),
276         "Cannot read a typed event record type field at offset %" PRId64 ".",
277         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(OffsetPtr, R.Size))
285     return createStringError(
286         std::make_error_code(std::errc::bad_address),
287         "Cannot read %d bytes of custom event data from offset %" PRId64 ".",
288         R.Size, OffsetPtr);
289 
290   std::vector<uint8_t> Buffer;
291   Buffer.resize(R.Size);
292   PreReadOffset = OffsetPtr;
293   if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
294     return createStringError(
295         std::make_error_code(std::errc::invalid_argument),
296         "Failed reading data into buffer of size %d at offset %" PRId64 ".",
297         R.Size, OffsetPtr);
298 
299   assert(OffsetPtr >= PreReadOffset);
300   if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
301     return createStringError(
302         std::make_error_code(std::errc::invalid_argument),
303         "Failed reading enough bytes for the typed event payload -- read "
304         "%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
305         OffsetPtr - PreReadOffset, R.Size, PreReadOffset);
306 
307   R.Data.assign(Buffer.begin(), Buffer.end());
308   return Error::success();
309 }
310 
visit(CallArgRecord & R)311 Error RecordInitializer::visit(CallArgRecord &R) {
312   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
313                                     MetadataRecord::kMetadataBodySize))
314     return createStringError(
315         std::make_error_code(std::errc::bad_address),
316         "Invalid offset for a call argument record (%" PRId64 ").",
317         OffsetPtr);
318 
319   auto PreReadOffset = OffsetPtr;
320   R.Arg = E.getU64(&OffsetPtr);
321   if (PreReadOffset == OffsetPtr)
322     return createStringError(
323         std::make_error_code(std::errc::invalid_argument),
324         "Cannot read a call arg record at offset %" PRId64 ".", OffsetPtr);
325 
326   OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
327   return Error::success();
328 }
329 
visit(PIDRecord & R)330 Error RecordInitializer::visit(PIDRecord &R) {
331   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
332                                     MetadataRecord::kMetadataBodySize))
333     return createStringError(
334         std::make_error_code(std::errc::bad_address),
335         "Invalid offset for a process ID record (%" PRId64 ").", OffsetPtr);
336 
337   auto PreReadOffset = OffsetPtr;
338   R.PID = E.getSigned(&OffsetPtr, 4);
339   if (PreReadOffset == OffsetPtr)
340     return createStringError(
341         std::make_error_code(std::errc::invalid_argument),
342         "Cannot read a process ID record at offset %" PRId64 ".", OffsetPtr);
343 
344   OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
345   return Error::success();
346 }
347 
visit(NewBufferRecord & R)348 Error RecordInitializer::visit(NewBufferRecord &R) {
349   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
350                                     MetadataRecord::kMetadataBodySize))
351     return createStringError(
352         std::make_error_code(std::errc::bad_address),
353         "Invalid offset for a new buffer record (%" PRId64 ").", OffsetPtr);
354 
355   auto PreReadOffset = OffsetPtr;
356   R.TID = E.getSigned(&OffsetPtr, sizeof(int32_t));
357   if (PreReadOffset == OffsetPtr)
358     return createStringError(
359         std::make_error_code(std::errc::invalid_argument),
360         "Cannot read a new buffer record at offset %" PRId64 ".", OffsetPtr);
361 
362   OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
363   return Error::success();
364 }
365 
visit(EndBufferRecord & R)366 Error RecordInitializer::visit(EndBufferRecord &R) {
367   if (!E.isValidOffsetForDataOfSize(OffsetPtr,
368                                     MetadataRecord::kMetadataBodySize))
369     return createStringError(
370         std::make_error_code(std::errc::bad_address),
371         "Invalid offset for an end-of-buffer record (%" PRId64 ").",
372         OffsetPtr);
373 
374   OffsetPtr += MetadataRecord::kMetadataBodySize;
375   return Error::success();
376 }
377 
visit(FunctionRecord & R)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                             --OffsetPtr, FunctionRecord::kFunctionRecordSize))
389     return createStringError(
390         std::make_error_code(std::errc::bad_address),
391         "Invalid offset for a function record (%" PRId64 ").", OffsetPtr);
392 
393   auto BeginOffset = OffsetPtr;
394   auto PreReadOffset = BeginOffset;
395   uint32_t Buffer = E.getU32(&OffsetPtr);
396   if (PreReadOffset == OffsetPtr)
397     return createStringError(
398         std::make_error_code(std::errc::bad_address),
399         "Cannot read function id field from offset %" PRId64 ".", 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         std::make_error_code(std::errc::invalid_argument),
415         "Unknown function record type '%d' at offset %" PRId64 ".",
416         FunctionType, BeginOffset);
417   }
418 
419   R.FuncId = Buffer >> 4;
420   PreReadOffset = OffsetPtr;
421   R.Delta = E.getU32(&OffsetPtr);
422   if (OffsetPtr == PreReadOffset)
423     return createStringError(
424         std::make_error_code(std::errc::invalid_argument),
425         "Failed reading TSC delta from offset %" PRId64 ".", OffsetPtr);
426   assert(FunctionRecord::kFunctionRecordSize == (OffsetPtr - BeginOffset));
427   return Error::success();
428 }
429 
430 } // namespace xray
431 } // namespace llvm
432