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(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
30Error 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
59Error 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
84Error 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
102Error 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
175Error 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
239Error 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
311Error 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
330Error 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
348Error 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
366Error 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
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                            --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