1 //===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
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
7 //===----------------------------------------------------------------------===//
8 #include "llvm/XRay/FDRRecordProducer.h"
9 #include "llvm/Support/DataExtractor.h"
18 // Keep this in sync with the values written in the XRay FDR mode runtime in
20 enum MetadataRecordKinds
: uint8_t {
26 CustomEventMarkerKind
,
31 // This is an end marker, used to identify the upper bound for this enum.
35 Expected
<std::unique_ptr
<Record
>>
36 metadataRecordType(const XRayFileHeader
&Header
, uint8_t T
) {
38 if (T
>= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker
))
39 return createStringError(std::make_error_code(std::errc::invalid_argument
),
40 "Invalid metadata record type: %d", T
);
42 case MetadataRecordKinds::NewBufferKind
:
43 return make_unique
<NewBufferRecord
>();
44 case MetadataRecordKinds::EndOfBufferKind
:
45 if (Header
.Version
>= 2)
46 return createStringError(
47 std::make_error_code(std::errc::executable_format_error
),
48 "End of buffer records are no longer supported starting version "
50 return make_unique
<EndBufferRecord
>();
51 case MetadataRecordKinds::NewCPUIdKind
:
52 return make_unique
<NewCPUIDRecord
>();
53 case MetadataRecordKinds::TSCWrapKind
:
54 return make_unique
<TSCWrapRecord
>();
55 case MetadataRecordKinds::WalltimeMarkerKind
:
56 return make_unique
<WallclockRecord
>();
57 case MetadataRecordKinds::CustomEventMarkerKind
:
58 if (Header
.Version
>= 5)
59 return make_unique
<CustomEventRecordV5
>();
60 return make_unique
<CustomEventRecord
>();
61 case MetadataRecordKinds::CallArgumentKind
:
62 return make_unique
<CallArgRecord
>();
63 case MetadataRecordKinds::BufferExtentsKind
:
64 return make_unique
<BufferExtents
>();
65 case MetadataRecordKinds::TypedEventMarkerKind
:
66 return make_unique
<TypedEventRecord
>();
67 case MetadataRecordKinds::PidKind
:
68 return make_unique
<PIDRecord
>();
69 case MetadataRecordKinds::EnumEndMarker
:
70 llvm_unreachable("Invalid MetadataRecordKind");
72 llvm_unreachable("Unhandled MetadataRecordKinds enum value");
75 constexpr bool isMetadataIntroducer(uint8_t FirstByte
) {
76 return FirstByte
& 0x01u
;
81 Expected
<std::unique_ptr
<Record
>>
82 FileBasedRecordProducer::findNextBufferExtent() {
83 // We seek one byte at a time until we find a suitable buffer extents metadata
85 std::unique_ptr
<Record
> R
;
87 auto PreReadOffset
= OffsetPtr
;
88 uint8_t FirstByte
= E
.getU8(&OffsetPtr
);
89 if (OffsetPtr
== PreReadOffset
)
90 return createStringError(
91 std::make_error_code(std::errc::executable_format_error
),
92 "Failed reading one byte from offset %" PRId64
".", OffsetPtr
);
94 if (isMetadataIntroducer(FirstByte
)) {
95 auto LoadedType
= FirstByte
>> 1;
96 if (LoadedType
== MetadataRecordKinds::BufferExtentsKind
) {
97 auto MetadataRecordOrErr
= metadataRecordType(Header
, LoadedType
);
98 if (!MetadataRecordOrErr
)
99 return MetadataRecordOrErr
.takeError();
101 R
= std::move(MetadataRecordOrErr
.get());
102 RecordInitializer
RI(E
, OffsetPtr
);
103 if (auto Err
= R
->apply(RI
))
104 return std::move(Err
);
109 llvm_unreachable("Must always terminate with either an error or a record.");
112 Expected
<std::unique_ptr
<Record
>> FileBasedRecordProducer::produce() {
113 // First, we set up our result record.
114 std::unique_ptr
<Record
> R
;
116 // Before we do any further reading, we should check whether we're at the end
117 // of the current buffer we're been consuming. In FDR logs version >= 3, we
118 // rely on the buffer extents record to determine how many bytes we should be
119 // considering as valid records.
120 if (Header
.Version
>= 3 && CurrentBufferBytes
== 0) {
121 // Find the next buffer extents record.
122 auto BufferExtentsOrError
= findNextBufferExtent();
123 if (!BufferExtentsOrError
)
125 BufferExtentsOrError
.takeError(),
127 std::make_error_code(std::errc::executable_format_error
),
128 "Failed to find the next BufferExtents record."));
130 R
= std::move(BufferExtentsOrError
.get());
131 assert(R
!= nullptr);
132 assert(isa
<BufferExtents
>(R
.get()));
133 auto BE
= dyn_cast
<BufferExtents
>(R
.get());
134 CurrentBufferBytes
= BE
->size();
139 // At the top level, we read one byte to determine the type of the record to
140 // create. This byte will comprise of the following bits:
142 // - offset 0: A '1' indicates a metadata record, a '0' indicates a function
144 // - offsets 1-7: For metadata records, this will indicate the kind of
145 // metadata record should be loaded.
147 // We read first byte, then create the appropriate type of record to consume
148 // the rest of the bytes.
149 auto PreReadOffset
= OffsetPtr
;
150 uint8_t FirstByte
= E
.getU8(&OffsetPtr
);
151 if (OffsetPtr
== PreReadOffset
)
152 return createStringError(
153 std::make_error_code(std::errc::executable_format_error
),
154 "Failed reading one byte from offset %" PRId64
".", OffsetPtr
);
156 // For metadata records, handle especially here.
157 if (isMetadataIntroducer(FirstByte
)) {
158 auto LoadedType
= FirstByte
>> 1;
159 auto MetadataRecordOrErr
= metadataRecordType(Header
, LoadedType
);
160 if (!MetadataRecordOrErr
)
162 MetadataRecordOrErr
.takeError(),
164 std::make_error_code(std::errc::executable_format_error
),
165 "Encountered an unsupported metadata record (%d) "
166 "at offset %" PRId64
".",
167 LoadedType
, PreReadOffset
));
168 R
= std::move(MetadataRecordOrErr
.get());
170 R
= llvm::make_unique
<FunctionRecord
>();
172 RecordInitializer
RI(E
, OffsetPtr
);
174 if (auto Err
= R
->apply(RI
))
175 return std::move(Err
);
177 // If we encountered a BufferExtents record, we should record the remaining
178 // bytes for the current buffer, to determine when we should start ignoring
179 // potentially malformed data and looking for buffer extents records.
180 if (auto BE
= dyn_cast
<BufferExtents
>(R
.get())) {
181 CurrentBufferBytes
= BE
->size();
182 } else if (Header
.Version
>= 3) {
183 if (OffsetPtr
- PreReadOffset
> CurrentBufferBytes
)
184 return createStringError(
185 std::make_error_code(std::errc::executable_format_error
),
186 "Buffer over-read at offset %" PRId64
" (over-read by %" PRId64
187 " bytes); Record Type = %s.",
188 OffsetPtr
, (OffsetPtr
- PreReadOffset
) - CurrentBufferBytes
,
189 Record::kindToString(R
->getRecordType()).data());
191 CurrentBufferBytes
-= OffsetPtr
- PreReadOffset
;
193 assert(R
!= nullptr);