1 //===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
9 #include "llvm/XRay/FDRRecordProducer.h"
10 #include "llvm/Support/DataExtractor.h"
19 // Keep this in sync with the values written in the XRay FDR mode runtime in
21 enum MetadataRecordKinds : uint8_t {
27 CustomEventMarkerKind,
32 // This is an end marker, used to identify the upper bound for this enum.
36 Expected<std::unique_ptr<Record>>
37 metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
39 if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
40 return createStringError(std::make_error_code(std::errc::invalid_argument),
41 "Invalid metadata record type: %d", T);
43 case MetadataRecordKinds::NewBufferKind:
44 return make_unique<NewBufferRecord>();
45 case MetadataRecordKinds::EndOfBufferKind:
46 if (Header.Version >= 2)
47 return createStringError(
48 std::make_error_code(std::errc::executable_format_error),
49 "End of buffer records are no longer supported starting version "
51 return make_unique<EndBufferRecord>();
52 case MetadataRecordKinds::NewCPUIdKind:
53 return make_unique<NewCPUIDRecord>();
54 case MetadataRecordKinds::TSCWrapKind:
55 return make_unique<TSCWrapRecord>();
56 case MetadataRecordKinds::WalltimeMarkerKind:
57 return make_unique<WallclockRecord>();
58 case MetadataRecordKinds::CustomEventMarkerKind:
59 if (Header.Version >= 5)
60 return make_unique<CustomEventRecordV5>();
61 return make_unique<CustomEventRecord>();
62 case MetadataRecordKinds::CallArgumentKind:
63 return make_unique<CallArgRecord>();
64 case MetadataRecordKinds::BufferExtentsKind:
65 return make_unique<BufferExtents>();
66 case MetadataRecordKinds::TypedEventMarkerKind:
67 return make_unique<TypedEventRecord>();
68 case MetadataRecordKinds::PidKind:
69 return make_unique<PIDRecord>();
70 case MetadataRecordKinds::EnumEndMarker:
71 llvm_unreachable("Invalid MetadataRecordKind");
73 llvm_unreachable("Unhandled MetadataRecordKinds enum value");
76 constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
77 return FirstByte & 0x01u;
82 Expected<std::unique_ptr<Record>>
83 FileBasedRecordProducer::findNextBufferExtent() {
84 // We seek one byte at a time until we find a suitable buffer extents metadata
86 std::unique_ptr<Record> R;
88 auto PreReadOffset = OffsetPtr;
89 uint8_t FirstByte = E.getU8(&OffsetPtr);
90 if (OffsetPtr == PreReadOffset)
91 return createStringError(
92 std::make_error_code(std::errc::executable_format_error),
93 "Failed reading one byte from offset %d.", OffsetPtr);
95 if (isMetadataIntroducer(FirstByte)) {
96 auto LoadedType = FirstByte >> 1;
97 if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {
98 auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
99 if (!MetadataRecordOrErr)
100 return MetadataRecordOrErr.takeError();
102 R = std::move(MetadataRecordOrErr.get());
103 RecordInitializer RI(E, OffsetPtr);
104 if (auto Err = R->apply(RI))
105 return std::move(Err);
110 llvm_unreachable("Must always terminate with either an error or a record.");
113 Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
114 // First, we set up our result record.
115 std::unique_ptr<Record> R;
117 // Before we do any further reading, we should check whether we're at the end
118 // of the current buffer we're been consuming. In FDR logs version >= 3, we
119 // rely on the buffer extents record to determine how many bytes we should be
120 // considering as valid records.
121 if (Header.Version >= 3 && CurrentBufferBytes == 0) {
122 // Find the next buffer extents record.
123 auto BufferExtentsOrError = findNextBufferExtent();
124 if (!BufferExtentsOrError)
126 BufferExtentsOrError.takeError(),
128 std::make_error_code(std::errc::executable_format_error),
129 "Failed to find the next BufferExtents record."));
131 R = std::move(BufferExtentsOrError.get());
132 assert(R != nullptr);
133 assert(isa<BufferExtents>(R.get()));
134 auto BE = dyn_cast<BufferExtents>(R.get());
135 CurrentBufferBytes = BE->size();
140 // At the top level, we read one byte to determine the type of the record to
141 // create. This byte will comprise of the following bits:
143 // - offset 0: A '1' indicates a metadata record, a '0' indicates a function
145 // - offsets 1-7: For metadata records, this will indicate the kind of
146 // metadata record should be loaded.
148 // We read first byte, then create the appropriate type of record to consume
149 // the rest of the bytes.
150 auto PreReadOffset = OffsetPtr;
151 uint8_t FirstByte = E.getU8(&OffsetPtr);
152 if (OffsetPtr == PreReadOffset)
153 return createStringError(
154 std::make_error_code(std::errc::executable_format_error),
155 "Failed reading one byte from offset %d.", OffsetPtr);
157 // For metadata records, handle especially here.
158 if (isMetadataIntroducer(FirstByte)) {
159 auto LoadedType = FirstByte >> 1;
160 auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
161 if (!MetadataRecordOrErr)
163 MetadataRecordOrErr.takeError(),
165 std::make_error_code(std::errc::executable_format_error),
166 "Encountered an unsupported metadata record (%d) at offset %d.",
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 %d (over-read by %d bytes); Record Type "
188 OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes,
189 Record::kindToString(R->getRecordType()).data());
191 CurrentBufferBytes -= OffsetPtr - PreReadOffset;
193 assert(R != nullptr);