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/FDRRecordProducer.h"
9 #include "llvm/Support/DataExtractor.h"
10
11 #include <cstdint>
12
13 namespace llvm {
14 namespace xray {
15
16 namespace {
17
18 // Keep this in sync with the values written in the XRay FDR mode runtime in
19 // compiler-rt.
20 enum MetadataRecordKinds : uint8_t {
21 NewBufferKind,
22 EndOfBufferKind,
23 NewCPUIdKind,
24 TSCWrapKind,
25 WalltimeMarkerKind,
26 CustomEventMarkerKind,
27 CallArgumentKind,
28 BufferExtentsKind,
29 TypedEventMarkerKind,
30 PidKind,
31 // This is an end marker, used to identify the upper bound for this enum.
32 EnumEndMarker,
33 };
34
35 Expected<std::unique_ptr<Record>>
metadataRecordType(const XRayFileHeader & Header,uint8_t T)36 metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
37
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);
41 switch (T) {
42 case MetadataRecordKinds::NewBufferKind:
43 return std::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 "
49 "2 of the log.");
50 return std::make_unique<EndBufferRecord>();
51 case MetadataRecordKinds::NewCPUIdKind:
52 return std::make_unique<NewCPUIDRecord>();
53 case MetadataRecordKinds::TSCWrapKind:
54 return std::make_unique<TSCWrapRecord>();
55 case MetadataRecordKinds::WalltimeMarkerKind:
56 return std::make_unique<WallclockRecord>();
57 case MetadataRecordKinds::CustomEventMarkerKind:
58 if (Header.Version >= 5)
59 return std::make_unique<CustomEventRecordV5>();
60 return std::make_unique<CustomEventRecord>();
61 case MetadataRecordKinds::CallArgumentKind:
62 return std::make_unique<CallArgRecord>();
63 case MetadataRecordKinds::BufferExtentsKind:
64 return std::make_unique<BufferExtents>();
65 case MetadataRecordKinds::TypedEventMarkerKind:
66 return std::make_unique<TypedEventRecord>();
67 case MetadataRecordKinds::PidKind:
68 return std::make_unique<PIDRecord>();
69 case MetadataRecordKinds::EnumEndMarker:
70 llvm_unreachable("Invalid MetadataRecordKind");
71 }
72 llvm_unreachable("Unhandled MetadataRecordKinds enum value");
73 }
74
isMetadataIntroducer(uint8_t FirstByte)75 constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
76 return FirstByte & 0x01u;
77 }
78
79 } // namespace
80
81 Expected<std::unique_ptr<Record>>
findNextBufferExtent()82 FileBasedRecordProducer::findNextBufferExtent() {
83 // We seek one byte at a time until we find a suitable buffer extents metadata
84 // record introducer.
85 std::unique_ptr<Record> R;
86 while (!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);
93
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();
100
101 R = std::move(MetadataRecordOrErr.get());
102 RecordInitializer RI(E, OffsetPtr);
103 if (auto Err = R->apply(RI))
104 return std::move(Err);
105 return std::move(R);
106 }
107 }
108 }
109 llvm_unreachable("Must always terminate with either an error or a record.");
110 }
111
produce()112 Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
113 // First, we set up our result record.
114 std::unique_ptr<Record> R;
115
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)
124 return joinErrors(
125 BufferExtentsOrError.takeError(),
126 createStringError(
127 std::make_error_code(std::errc::executable_format_error),
128 "Failed to find the next BufferExtents record."));
129
130 R = std::move(BufferExtentsOrError.get());
131 assert(R != nullptr);
132 assert(isa<BufferExtents>(R.get()));
133 auto BE = cast<BufferExtents>(R.get());
134 CurrentBufferBytes = BE->size();
135 return std::move(R);
136 }
137
138 //
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:
141 //
142 // - offset 0: A '1' indicates a metadata record, a '0' indicates a function
143 // record.
144 // - offsets 1-7: For metadata records, this will indicate the kind of
145 // metadata record should be loaded.
146 //
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);
155
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)
161 return joinErrors(
162 MetadataRecordOrErr.takeError(),
163 createStringError(
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());
169 } else {
170 R = std::make_unique<FunctionRecord>();
171 }
172 RecordInitializer RI(E, OffsetPtr);
173
174 if (auto Err = R->apply(RI))
175 return std::move(Err);
176
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());
190
191 CurrentBufferBytes -= OffsetPtr - PreReadOffset;
192 }
193 assert(R != nullptr);
194 return std::move(R);
195 }
196
197 } // namespace xray
198 } // namespace llvm
199